JavaScript进阶之前端文件上传和下载示例详解

目录
  • 文件下载
    • 1.通过a标签点击直接下载
    • 2.open或location.href
    • 3.Blob和Base64
  • 文件上传
    • 文件上传思路
    • File文件
    • 上传单个文件-客户端
    • 上传文件-服务端
    • 多文件上传-客户端
    • 大文件上传-客户端
    • 大文件上传-服务端

文件下载

1.通过a标签点击直接下载

<a href="https:xxx.xlsx" rel="external nofollow"  download="test">下载文件</a>

download属性标识文件需要下载且下载名称为test

如果有 Content-Disposition 响应头,则不需要设置download属性就能下载,文件名在响应头里面由后端控制

此方法有同源和请求headers鉴权的问题

2.open或location.href

window.open('xxx.zip');
location.href = 'xxx.zip';

需要注意 url 长度和编码问题

不能直接下载浏览器默认预览的文件,如txt、图片

3.Blob和Base64

function downloadFile(res, Filename) {
  // res为接口返回数据,在请求接口的时候可进行鉴权
  if (!res) return;
  // IE及IE内核浏览器
  if ("msSaveOrOpenBlob" in navigator) {
    navigator.msSaveOrOpenBlob(res, name);
    return;
  }
  const url = URL.createObjectURL(new Blob([res]));
  //  const fileReader = new FileReader();  使用 Base64 编码生成
  // fileReader.readAsDataURL(res);
  // fileReader.onload = function() { ...此处逻辑和下面创建a标签并释放代码一致,可从fileReader.result获取href值...}
  const a = document.createElement("a");
  a.style.display = "none";
  a.href = url;
  a.download = Filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url); // 释放blob对象
}

注意 请求发送的时候注明 responseType = "blob",如无设置则需要 new Blob的时候传入第二个参数,如

new Blob([res], { type: xhr.getResponseHeader("Content-Type") });

此方法可以解决请求headers鉴权和下载浏览器默认直接预览的文件,并得知下载进度

文件上传

文件上传思路

File文件

  • MDN描述

上传单个文件-客户端

<input id="uploadFile" type="file" accept="image/*" />
  • type属性file:用户选择文件
  • accept属性:规定选择文件的类型

<body>
    <input id="uploadFile" type="file" accept="image/*" />
    <button type="button" id="uploadBtn" onClick="startUpload()">开始上传</button>
    <div class="progress">上传进度:<span id="progressValue">0</span></div>
    <div id="uploadResult" class="result"></div>
    <script>
      const uploadFileEle = document.getElementById("uploadFile");
      const progressValueEle = document.getElementById("progressValue");
      const uploadResultEle = document.getElementById("uploadResult");
      try {
        function startUpload() {
          if (!uploadFileEle.files.length) return;
          // 获取文件
          const file = uploadFileEle.files[0];
          // 创建上传数据
          const formData = new FormData();
          formData.append("file", file);
          // 上传文件
          upload(formData);
        }
        function upload(data) {
          const xhr = new XMLHttpRequest();
          xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {
              const result = JSON.parse(xhr.responseText);
              console.log("result:", result);
              uploadResultEle.innerText = xhr.responseText;
            }
          };
          // 上传进度
          xhr.upload.onprogress = function (event) {
            if (event.lengthComputable) {
              progressValueEle.innerText = Math.ceil((event.loaded * 100) / event.total) + "%";
            }
          };
          xhr.open("POST", "http://127.0.0.1:3000/upload", true);
          xhr.send(data);
        }
      } catch (e) {
        console.log("error:", e);
      }
    </script>
</body>

上传文件-服务端

  • 客户端使用form-data传递,服务端使用相同方式接受解析
  • 使用 multer 库处理 multipart/form-data

const app = express();
// 上传成功后返回URL地址
const resourceUrl = `http://127.0.0.1:${port}/`;
// 存储文件目录
const uploadDIr = path.join(__dirname, "/upload");
// destination 设置资源保存路径,filename 设置资源名称
const storage = multer.diskStorage({
  destination: async function (_req, _file, cb) {
    cb(null, uploadDIr);
  },
  filename: function (_req, file, cb) {
    // 设置文件名
    cb(null, `${file.originalname}`);
  },
});
const multerUpload = multer({ storage });
//设置静态访问目录
app.use(express.static(uploadDIr));
app.post("/upload", multerUpload.any(), function (req, res, _next) {
  // req.file 是 `avatar` 文件的信息
  let urls = [];
  //获取所有已上传的文件
  const files = req.files;
  if (files && files.length > 0) {
    //遍历生成url 集合返回给客户端
    urls = files.map((item, _key) => {
      return resourceUrl + item.originalname;
    });
  }
  return res.json({
    REV: true,
    DATA: {
      url: urls,
    },
    MSG: "成功",
  });
});

多文件上传-客户端

  • input属性:multiple是否允许多个值(相关类型emailfile )
 <body>
    <input id="uploadFile" type="file" accept="image/*" multiple />
    <button id="uploadBtn" onClick="startUpload()">开始上传</button>
    <div class="progress">上传进度:<span id="progressValue">0</span></div>
    <div id="uploadResult" class="result"></div>
    <script>
      const uploadFileEle = document.getElementById("uploadFile");
      const progressValueEle = document.getElementById("progressValue");
      const uploadResultEle = document.getElementById("uploadResult");
      try {
        function startUpload() {
          if (!uploadFileEle.files.length) return;
          //获取文件
          const files = uploadFileEle.files;
          const formData = this.getUploadData(files);
          this.upload(formData);
        }
        //添加多个文件
        function getUploadData(files) {
          const formData = new FormData();
          for (let i = 0; i < files.length; i++) {
            const file = files[i];
            formData.append(file.name, file);
          }
          return formData;
        }
        function upload(data) {
          const xhr = new XMLHttpRequest();
          xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {
              const result = JSON.parse(xhr.responseText);
              console.log("result:", result);
              uploadResultEle.innerText = xhr.responseText;
            }
          };
          xhr.upload.addEventListener(
            "progress",
            function (event) {
              if (event.lengthComputable) {
                progressValueEle.innerText = Math.ceil((event.loaded * 100) / event.total) + "%";
              }
            },
            false
          );
          xhr.open("POST", "http://127.0.0.1:3000/upload", true);
          xhr.send(data);
        }
      } catch (e) {
        console.log("error:", e);
      }
    </script>
  </body>

大文件上传-客户端

<body>
    <input id="uploadFile" type="file" />
    <button type="button" id="uploadBtn" onClick="startUpload()">开始上传</button>
    <div class="progress">上传进度:<span id="progressValue">0</span></div>
    <div id="uploadResult" class="result"></div>
    <script src="./fileUtils.js"></script>
    <script src="./spark-md5.min.js"></script>
    <script src="./index.js"></script>
    <script>
      const uploadFileEle = document.getElementById("uploadFile");
      const progressValueEle = document.getElementById("progressValue");
      const uploadResultEle = document.getElementById("uploadResult");
      try {
        function startUpload() {
          if (!uploadFileEle.files.length) return;
          //获取文件
          const file = uploadFileEle.files[0];
          window.upload.start(file);
        }
      } catch (e) {
        console.log("error:", e);
      }
    </script>
  </body>

fileUtils

// 文件分片
function handleFileChunk(file, chunkSize) {
    const fileChunkList = [];
    // 索引值
    let curIndex = 0;
    while (curIndex < file.size) {
        // 最后一个切片以实际结束大小为准。
        const endIndex = curIndex + chunkSize < file.size ? curIndex + chunkSize : file.size;
        // 截取当前切片大小
        const curFileChunkFile = file.slice(curIndex, endIndex);
        // 更新当前索引
        curIndex += chunkSize;
        fileChunkList.push({ file: curFileChunkFile });
    }
    return fileChunkList;
}
//设置默认切片大小为5M
const DefaultChunkSize = 5 * 1024 * 1024;
const start = async function (bigFile) {
  // 生成多个切片
  const fileList = handleFileChunk(bigFile, DefaultChunkSize);
  // 获取整个大文件的内容hash,方便实现秒传
  // const containerHash = await getFileHash(fileList);
  const containerHash = await getFileHash2(bigFile);
  // 给每个切片添加辅助内容信息
  const chunksInfo = fileList.map(({ file }, index) => ({
    // 整个文件hash
    fileHash: containerHash,
    // 当前切片的hash
    hash: containerHash + "-" + index,
    // 当前是第几个切片
    index,
    // 文件个数
    fileCount: fileList.length,
    // 切片内容
    chunk: file,
    // 文件总体大小
    totalSize: bigFile.size,
    // 单个文件大小
    size: file.size,
  }));
  //上传所有文件
  uploadChunks(chunksInfo, bigFile.name);
};
/**
 *
 * 获取全部文件内容hash
 * @param {any} fileList
 */
async function getFileHash(fileList) {
  console.time("filehash");
  const spark = new SparkMD5.ArrayBuffer();
  // 获取全部内容
  const result = fileList.map((item, key) => {
    return getFileContent(item.file);
  });
  try {
    const contentList = await Promise.all(result);
    for (let i = 0; i < contentList.length; i++) {
      spark.append(contentList[i]);
    }
    // 生成指纹
    const res = spark.end();
    console.timeEnd("filehash");
    return res;
  } catch (e) {
    console.log(e);
  }
}
/**
 *
 * 获取全部文件内容hash
 * @param {any} fileList
 */
async function getFileHash2(fileList) {
  console.time("filehash");
  const spark = new SparkMD5.ArrayBuffer();
  // 获取全部内容
  const content = await getFileContent(fileList);
  try {
    spark.append(content);
    // 生成指纹
    const result = spark.end();
    console.timeEnd("filehash");
    return result;
  } catch (e) {
    console.log(e);
  }
}
/**
 *
 * 获取文件内容
 * @param {any} file
 */
function getFileContent(file) {
  return new Promise((resolve, reject) => {
    const fileReader = new FileReader();
    // 读取文件内容
    fileReader.readAsArrayBuffer(file);
    fileReader.onload = (e) => {
      // 返回读取到的文件内容
      resolve(e.target.result);
    };
    fileReader.onerror = (e) => {
      reject(fileReader.error);
      fileReader.abort();
    };
  });
}
/**
 *
 * 上传所有的分片
 * @param {any} chunks
 * @param {any} fileName
 */
async function uploadChunks(chunks, fileName) {
  const requestList = chunks
    .map(({ chunk, hash, fileHash, index, fileCount, size, totalSize }) => {
      //生成每个切片上传的信息
      const formData = new FormData();
      formData.append("hash", hash);
      formData.append("index", index);
      formData.append("fileCount", fileCount);
      formData.append("size", size);
      formData.append("splitSize", DefaultChunkSize);
      formData.append("fileName", fileName);
      formData.append("fileHash", fileHash);
      formData.append("chunk", chunk);
      formData.append("totalSize", totalSize);
      return { formData, index };
    })
    .map(async ({ formData, index }) =>
      singleRequest({
        url: "http://127.0.0.1:3000/uploadBigFile",
        data: formData,
      })
    );
  //全部上传
  await Promise.all(requestList);
}
/**
 * 单个文件上传
 */
function singleRequest({ url, method = "post", data, headers = {} }) {
  return new Promise((resolve) => {
    const xhr = new XMLHttpRequest();
    xhr.open(method, url);
    Object.keys(headers).forEach((key) => xhr.setRequestHeader(key, headers[key]));
    xhr.send(data);
    xhr.onload = (e) => {
      resolve({
        data: e.target.response,
      });
    };
  });
}
window.upload = {
  start: start,
};

大文件上传-服务端

...
import { checkFileIsMerge, chunkMerge } from "./upload";
const multiparty = require("multiparty");
const fse = require("fs-extra");
// 上传成功后返回URL地址
const resourceUrl = `http://127.0.0.1:${port}/`;
// 存储文件目录
const uploadDIr = path.join(__dirname, "/upload");
//设置静态访问目录
app.use(express.static(uploadDIr));
const extractExt = (filename) => filename.slice(filename.lastIndexOf("."), filename.length); // 提取后缀名
app.post("/uploadBigFile", function (req, res, _next) {
  const multipart = new multiparty.Form();
  multipart.parse(req, async (err, fields, files) => {
    if (err) {
      console.error(err);
      return res.json({
        code: 5000,
        data: null,
        msg: "上传文件失败",
      });
    }
    //取出文件内容
    const [chunk] = files.chunk;
    //当前chunk 文件hash
    const [hash] = fields.hash;
    //大文件的hash
    const [fileHash] = fields.fileHash;
    //大文件的名称
    const [fileName] = fields.fileName;
    //切片索引
    const [index] = fields.index;
    //总共切片个数
    const [fileCount] = fields.fileCount;
    //当前chunk 的大小
    // const [size] = fields.size;
    const [splitSize] = fields.splitSize;
    //整个文件大小
    const [totalSize] = fields.totalSize;
    const saveFileName = `${fileHash}${extractExt(fileName)}`;
    //获取整个文件存储路径
    const filePath = path.resolve(uploadDIr, saveFileName);
    const chunkDir = path.resolve(uploadDIr, fileHash);
    // 大文件存在直接返回,根据内容hash存储,可以实现后续秒传
    if (fse.existsSync(filePath)) {
      return res.json({
        code: 1000,
        data: { url: `${resourceUrl}${saveFileName}` },
        msg: "上传文件已存在",
      });
    }
    // 切片目录不存在,创建切片目录
    if (!fse.existsSync(chunkDir)) {
      await fse.mkdirs(chunkDir);
    }
    const chunkFile = path.resolve(chunkDir, hash);
    if (!fse.existsSync(chunkFile)) {
      await fse.move(chunk.path, path.resolve(chunkDir, hash));
    }
    const isMerge = checkFileIsMerge(chunkDir, Number(fileCount), fileHash);
    if (isMerge) {
      //合并
      await chunkMerge({
        filePath: filePath,
        fileHash: fileHash,
        chunkDir: chunkDir,
        splitSize: Number(splitSize),
        fileCount: Number(fileCount),
        totalSize: Number(totalSize),
      });
      return res.json({
        code: 1000,
        data: { url: `${resourceUrl}${saveFileName}` },
        msg: "文件上传成功",
      });
    } else {
      return res.json({
        code: 200,
        data: { url: `${resourceUrl}${filePath}` },
        msg: "文件上传成功",
      });
    }
  });
});

upload.ts

const fse = require("fs-extra");
const path = require("path");
/**
 * 读流,写流
 * @param path
 * @param writeStream
 * @returns
 */
const pipeStream = (path, writeStream) =>
    new Promise((resolve) => {
        const readStream = fse.createReadStream(path);
        readStream.on("end", () => {
            // fse.unlinkSync(path);
            resolve(null);
        });
        readStream.pipe(writeStream);
    });
/**
 *
 * 合并所有切片
 * @export
 * @param {any} {
 *     filePath:文件路径包含后缀名
 *     fileHash:文件hash
 *     chunkDir:切片存放的临时目录
 *     splitSize:每个切片的大小
 *     fileCount:文件总个数
 *     totalSize:文件总大小
 * }
 * @returns
 */
export async function chunkMerge({
    filePath,
    fileHash,
    chunkDir,
    splitSize,
    fileCount,
    totalSize,
}) {
    const chunkPaths = await fse.readdir(chunkDir);
    //帅选合适的切片
    const filterPath = chunkPaths.filter((item) => {
        return item.includes(fileHash);
    });
    //数量不对,抛出错误
    if (filterPath.length !== fileCount) {
        console.log("合并错误");
        return;
    }
    // 根据切片下标进行排序,方便合并
    filterPath.sort((a, b) => a.split("-")[1] - b.split("-")[1]);
    await Promise.all(
        chunkPaths.map((chunkPath, index) => {
            //并发写入,需要知道开始和结束位置
            let end = (index + 1) * splitSize;
            if (index === fileCount - 1) {
                end = totalSize + 1;
            }
            return pipeStream(
                path.resolve(chunkDir, chunkPath),
                // 指定位置创建可写流
                fse.createWriteStream(filePath, {
                    start: index * splitSize,
                    end: end,
                })
            );
        })
    );
    //删除所有切片
    // fse.rmdirSync(chunkDir); // 合并后删除保存切片的目录
    return filePath;
}

/**
 *
 * 检查切片是否可以合并
 * @export
 * @param {any} pathName 切片存储目录
 * @param {any} totalCount 大文件包含切片个数
 * @param {any} hash 大文件hash
 * @returns
 */
export function checkFileIsMerge(pathName, totalCount, hash) {
    var dirs = [];
    //同步读取切片存储目录
    const readDir = fse.readdirSync(pathName);
    //判断目录下切片数量 小于 总切片数,不能合并
    if (readDir && readDir.length < totalCount) return false;
    //获取目录下所有真正属于该文件的切片,以大文件hash为准
    (function iterator(i) {
        if (i == readDir.length) {
            return;
        }
        const curFile = fse.statSync(path.join(pathName, readDir[i]));
        //提出目录和文件名不包含大文件hash的文件
        if (curFile.isFile() && readDir[i].includes(hash + "")) {
            dirs.push(readDir[i]);
        }
        iterator(i + 1);
    })(0);
    //数量一直,可以合并
    if (dirs.length === totalCount) {
        return true;
    }
    return false;
}

这里的大文件上传有几处问题,我没有解决,留给各位思考啦

  • 内容hash计算速度如何提升(serviceworker)
  • 文件上传进度
  • 断点续传

以上就是JavaScript进阶之前端文件上传和下载示例详解的详细内容,更多关于JavaScript前端文件上传下载的资料请关注我们其它相关文章!

(0)

相关推荐

  • node.js express框架实现文件上传与下载功能实例详解

    本文实例讲述了node.js express框架实现文件上传与下载功能.分享给大家供大家参考,具体如下: 背景 昨天吉视传媒的客户对IPS信息发布系统又提了一个新需求,就是发布端发送消息时需要支持附件的上传,而接收端可以对发布端上传的附件进行下载:接收端回复消息时也需要支持上传附件,发布端可以对所有接收端上传的附件进行打包下载. 功能实现 前台部分 前台使用webUploader插件即可,这是百度开发的一款文件上传组件,具体使用查看它的API即可.这个项目之前开发的时候前台使用了angular.

  • Jsp+Servlet实现文件上传下载 文件上传(一)

    文件上传和下载功能是Java Web必备技能,很实用. 本文使用的是Apache下的著名的文件上传组件 org.apache.commons.fileupload 实现 下面结合最近看到的一些资料以及自己的尝试,先写第一篇文件上传.后续会逐步实现下载,展示文件列表,上传信息持久化等. 废话少说,直接上代码 第一步,引用jar包,设置上传目录 commons-fileupload-1.3.1.jar commons-io-2.4.jar 上传目录:WEB-INF/tempFiles和WEB-INF

  • 原生js实现文件上传、下载、封装等实例方法

    一 .下载 1.代码 const fileDownloadClick = (obj) => { // 解决兼容 if( document.all ){ obj.click(); } else { let event = document.createEvent("MouseEvents"); event.initEvent('click', true, true); obj.dispatchEvent(event); } } const fileDownload = (res,o

  • Jsp+Servlet实现文件上传下载 文件列表展示(二)

    接着上一篇讲: Jsp+Servlet实现文件上传下载(一)--文件上传 本章来实现一下上传文件列表展示,同时优化了一下第一章中的代码. 废话少说,上代码 mysql创建附件表 DROP TABLE tbl_accessory; CREATE TABLE tbl_accessory ( id INT AUTO_INCREMENT PRIMARY KEY, file_name VARCHAR(500), file_size DOUBLE(10,2), file_ext_name VARCHAR(1

  • JSP servlet实现文件上传下载和删除

    本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 由于存储空间,对一般用户而言,就是用来操作存储文件的,所以这两天,特意看了一下windows下用servlet实现文件上传.下载和删除,下面是详细代码说明 上传: 用的是commons-fileupload-1.2.2.jar和commons-io-2.0.1.jar组件,可以去apache官网上去下载,然后放到WebRoot/WEB-INF/lib目录下 upload.html <html> <hea

  • Jsp+Servlet实现文件上传下载 删除上传文件(三)

    接着上一篇讲:Jsp+Servlet实现文件上传下载(二)--文件列表展示 本章来实现一下删除已上传文件,同时优化了一下第一章中的代码. 废话少说,上代码得意 1.调整列表页面list.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/j

  • JavaScript进阶之前端文件上传和下载示例详解

    目录 文件下载 1.通过a标签点击直接下载 2.open或location.href 3.Blob和Base64 文件上传 文件上传思路 File文件 上传单个文件-客户端 上传文件-服务端 多文件上传-客户端 大文件上传-客户端 大文件上传-服务端 文件下载 1.通过a标签点击直接下载 <a href="https:xxx.xlsx" rel="external nofollow" download="test">下载文件</

  • SpringBoot+微信小程序实现文件上传与下载功能详解

    目录 1.文件上传 1.1 后端部分 1.2 小程序前端部分 1.3 实现效果 2.文件下载 2.1 后端部分 2.2 小程序前端部分 2.3 实现效果 1.文件上传 1.1 后端部分 1.1.1 引入Apache Commons FIleUpload组件依赖 <!--文件上传与下载相关的依赖--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fil

  • JavaWeb实现文件上传与下载实例详解

    在Web应用程序开发中,文件上传与下载功能是非常常用的功能,下面通过本文给大家介绍JavaWeb实现文件上传与下载实例详解. 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择采用apache的开源工具common-fileupload这个文件上传组件.这个common-fileupload上传组件的jar包可以去apache官网上面下载,common-fileupload是依赖于c

  • Android关于FTP文件上传和下载功能详解

    本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 此篇博客为整理文章,供大家学习. 1.首先下载commons-net  jar包,可以百度下载. FTP的文件上传和下载的工具类: package ryancheng.example.progressbar; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.Outpu

  • python3 requests库文件上传与下载实现详解

    在接口测试学习过程中,遇到了利用requests库进行文件下载和上传的问题.同样,在真正的测试过程中,我们不可避免的会遇到上传和下载的测试. 文件上传: url = ztx.host+'upload/uploadFile?CSRFToken='+self.getCSRFToken()#上传文件的接口地址 header = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko', '

  • Apache 文件上传与文件下载案例详解

    写一个Apache文件上传与文件下载的案例:以供今后学习 web.xml配置如下: <span style="font-family:SimSun;font-size:14px;"><?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns=&

  • 基于BootStrap Metronic开发框架经验小结【五】Bootstrap File Input文件上传插件的用法详解

    Bootstrap文件上传插件File Input是一个不错的文件上传控件,但是搜索使用到的案例不多,使用的时候,也是一步一个脚印一样摸着石头过河,这个控件在界面呈现上,叫我之前使用过的Uploadify 好看一些,功能也强大些,本文主要基于我自己的框架代码案例,介绍其中文件上传插件File Input的使用. 1.文件上传插件File Input介绍 这个插件主页地址是:http://plugins.krajee.com/file-input,可以从这里看到很多Demo的代码展示:http:/

  • 网络安全漏洞渗透测试之文件上传绕过思路案例详解

    目录 引言 案例一 案例二 案例三 案例四 其它场景&总结 引言 分享一些文件上传绕过的思路,下文内容多包含实战图片,所以打码会非常严重,可多看文字表达:本文仅用于交流学习, 由于传播.利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任. 案例一 一次项目渗透时,通过往png后缀随便加个字符可知该上传点为白名单上传,正常情况下无法绕过 通过观察接口信息,发现接口名字为UploadImg,猜测该接口用于图片上传,按照开发的习惯,保不准会存在te

  • AngularJS 文件上传控件 ng-file-upload详解

    网上可以找到的 AngularJS 的文件上传控件有两个: angular-file-upload:https://github.com/nervgh/angular-file-upload ng-file-upload:https://github.com/danialfarid/ng-file-upload 这两个非常类似,连js文件的结构都是一样的.核心的js是.min.js,还都有一个-shim.min.js,用来支持上传进度条和上传暂停等高级功能. 按道理讲shim.js应该是可加可不

  • PHP实现的文件上传类与用法详解

    本文实例讲述了PHP实现的文件上传类与用法.分享给大家供大家参考,具体如下: FileUpload.class.php,其中用到了两个常量,可在网站配置文件中定义:define('ROOT_PATH',dirname(__FILE__)); //网站根目录.define('UPDIR','/uploads/'); //上传主目录 <?php //上传文件类 class FileUpload { private $error; //错误代码 private $maxsize; //表单最大值 pr

随机推荐