文件上传与下载

项目地址:文件上传与下载

1. 前置知识

具体详见「浏览器模型」中文件相关内容。

1.1 Blob 对象

Blob 对象表示一个二进制文件的数据内容,比如图片文件的内容就可以通过 Blob 对象读写。

// 创建 lob 对象
const blob = new Blob(array [, type]);

// 举例
const blob_1 = new Blob(['<a id="a"><b id="b">hey!</b></a>'], {type : 'text/html'});
const blob_2 = new Blob([ JSON.stringify({ hello: 'world' }) ], {type : 'application/json'});

第一个参数是数组,成员是字符串或二进制对象,表示新生成的Blob实例对象的内容;第二个参数是配置对象,目前只有一个属性type,表示数据的 MIME 类型。

Blob 对象有一个非常重要的方法myBlob.slice(start, end, contentType),其用来拷贝原来的数据,常用于大文件分片上传,详见大文件分片上传

获取 Blob 对象后,直接打印是获取不到的,我们需要通过FileReader对象,读取 Blob 对象的内容,即文件内容。

const reader = new FileReader();
reader.readAsText(myBlob); // 返回文本,需要指定文本编码,默认为 UTF-8。
reader.onload = function () {
  const text = reader.result; // 通过指定 FileReader 实例对象的onload监听函数,在实例的result属性上才拿到文件内容
}

浏览器允许使用URL.createObjectURL()方法,针对 Blob 对象生成一个临时 URL,以便于某些 API 使用。

1.2 File 对象

File 对象代表一个文件,用来读写文件信息。它继承了 Blob 对象,或者说是一种特殊的 Blob 对象,所有可以使用 Blob 对象的场合都可以使用它。最常见的使用场合是表单的文件上传控件(<input type="file">),用户选中文件以后,浏览器就会生成一个数组,里面是每一个用户选中的文件,它们都是 File 实例对象。

第一个参数是数组,成员可以是二进制对象或字符串,表示文件的内容;第二个参数表示文件名或文件路径;第三个参数是可选对象,设置实例的属性。

1.3 FileReader 对象

FileReader 对象用于读取 File 对象或 Blob 对象所包含的文件内容。

  • FileReader.readAsText():返回文本,需要指定文本编码,默认为 UTF-8。

  • FileReader.readAsArrayBuffer():返回 ArrayBuffer 对象。

  • FileReader.readAsDataURL():返回 Data URL。

  • FileReader.readAsBinaryString():返回原始的二进制字符串。

2. 文件上传

2.1 Form 表单提交

直接通过 form 表单提交,也不需要 JS,action 即为接口地址,注意两点,一是类型需要设置为multipart/form-data,二是 input必须设置 name 属性,否则数据无法发送。

2.2 文件接口上传

通过 document.getElementById('file').files[0] 获取 file 对象,再以 formData 形式发送请求。

file 打印出来如下所示:

formData 打印出来如下所示:

FormData 无法得到文件的内容,需要使用 FileReader 读取整个文件的内容:

2.3 大文件分片上传

分片上传思路:

  1. 将文件按一定大小(比如 1M)截取成一小份,并将切片带上 hash 值,用于作为标识,简单方式是文件名字+下标来标识,但是文件名修改就失去意义了,因此建议根据文件内容生成 hash,采用 spark-md5 库。

  2. 将每个切片文件,当前端获取到本地图片后,利用 Blob.prototype.slice 方法(和数组的 slice 方法相似),将大文件按照一定大小(例如 1M)进行切割,再并发将各个分片上传到服务端,服务端保存每个切片文件的信息。

  3. 切片上传完成后,服务端根据文件标识进行合并,合并完后删除切片文件。

断点续传思路:

  1. 每个分片上传的时候,服务端记录上传好的文件 hash 值,上传成功后返回 hash 值给前端,前端记录 hash 值

  2. 重新上传时,将每个文件的 hash 值与记录的 hash 值做比对,如果相同的话则跳过,继续下一个分段的上传。

  3. 全部分片上传完成后,服务端根据文件标识进行合并,合并完后删除小文件。

3. 文件下载

3.1 window.open

浏览器可直接浏览的文件类型是不提供下载的,如 txt、png、jpg、gif 等,会直接打开而非下载;

3.2 调用 form 表单的 submit

无法直接下载浏览器可直接预览的文件类型,无法知道下载的进度;

3.3 调用 a 标签的 click

3.4 Blob 对象下载

除了利用已知文件地址路径进行下载,还能够发送请求 api 获取文件流进行下载,利用 Blob 对象可以将文件流转化成 Blob 二进制对象。

基本思路:发请求获取二进制数据,转化为 Blob 对象,利用 URL.createObjectUrl 生成 url 地址,赋值在a标签的href属性上,结合 download 进行下载,即文件流 -> fileReader转Blob对象 -> a标签下载。

3.5 Base64 下载

base64 可以实现任意类型文件的下载,即文件流 -> fileReader转base64 -> a标签下载。

3.4 依赖其他库

通过第三方依赖包来实现,例如 file-saver 等。

4. 总结

4.1 文件上传

File 对象基于 input 标签通过document.getElementById('file').files[0]获取

  • formData 上传

  • Base64 通过字符串上传

4.2 文件下载

  • 调用 window.open(URL)

  • 表单 form.action = URL 调用 submit()

  • a 标签 href = URL 调用 click()

没有URL则需要创造URL。数据(例如文件流、json等)可以转成 Blob 对象,再通过 URL.createObjectURL(blob) 即可获得 URL。

objectURL = URL.createObjectURL(object); 的 object 表示指定的 File 对象、 Blob 对象或者 MediaSource 对象。

base64 可以直接当成 URL 来下载。

如果你对内容有任何疑问,欢迎提交 ❕issues✉️ email

最后更新于

这有帮助吗?