图片,音频,视频等等这几种常见的资源类型,如果需要从前端上传到服务端,有几种方式呢?不妨回顾一下经历过的项目想一想。
项目上也用到很多上传文件的地方,七牛云,阿里云 OSS,讯飞 web api 上传都接触过,所以在这里做一个记录,总结一下前端上传的几种方式。
- 上传基本概念
- 常见前端上传场景
- 自家后端服务上传浅析
- 阿里云 OSS 上传浅析
- 七牛云上传浅析
- 几种上传服务的对比
- 思考
上传基本概念
File
- File 对象可以通过
<input>
,DataTransfer, 或者HTMLCanvasElement.mozGetAsFile()
-
new File(bits, name[, options])
此处的 bits 是一个数组,数组元素可以由 ArrayBuffer,ArrayBufferView,Blob 或者 DOMString 组成;fileName 指明文件名称或者文件路径;options 里的 type 属性指明 MIME 类型,lastModified 属性指明上次修改时间,默认是 Date.now();
FormData
- 键值对形式的表单,可被 XMLHttpRequest.send()直接发送
- 编码格式指定为
multipart/form-data
也需要用 FormData 组装数据 其实就是 http 的 request header 指定了{Content-Type: 'multipart/form-data'}
- append(key,value)向 FormData 中添加数据,存在覆盖不存在新建
Blob 对象
可以参考我的另一个记录:blob Url 那些事儿
常见前端上传场景
- 原生 File 对象。使用场景:本地上传的图片,音频,视频等文件,没有经过任何其他处理。
- 转换后的 Blob 对象。使用场景:图片 canvas 合成后的 base64 转换为 Blob 对象;音频通过麦克风录音算法生成的 Uint8Array 转换成 Blob 对象。
- 字节数组。使用场景:音频通过麦克风录音算法生成 arrayBuffer 再转换成 Uint8Array。
tips:
- 七牛云 qiniu-js 和 阿里云 OSS ali-oss 支持 Blob 对象的上传方式。
- 讯飞 web api 文件分片上传使用字节数组的方式。
- 看似 File 对象和 Blob 对象是 2 种上传方式,但其实是 1 种上传方式,因为 File 对象继承自 Blob 对象,可以把上述两种上传统一理解成上传 Blob 对象。
自家后端上传服务浅析
const formData = new FormData(); // 由于服务端编码格式为 multipart/form-data,所以需要构造一个 FormData 对象
const file = new File([blobObj], fileName, {type: blobObj.type}); // 传入 blob 对象创建一个 File 实例
formData.append('file', file);// formData 实例增加 file 对象
const options = {
method: 'post',
url: 'https://foo.bar.com/baz',
headers: {'Content-Type': 'multipart/form-data'}, // 上传相关
data: formData, // 上传相关
};
axios.request(options).then(()=>{}).catch(()=>{});
阿里云 OSS 上传浅析
ali-oss 是一个 Node 环境和 Browser 环境皆可用的对象存储服务 package。
import OSS from 'ali-oss';
const token = apiNode.generateOssToken({type}) // 调用 nodejs 端接口,根据类型生成某个 bucket 有效的 token
this.client = new OSS(token);
const blobObj = new Blob([u8arr], {type: mime});
this.client
.put(fileName, blobObj) // 这里我们传入的值 blob 对象,ali-oss 仅支持浏览器端的 Blob 对象
.then((data) => {if (data && data.name) {return data.name; // OSS 返回的文件名}
})
.catch(() => {});
可以再伪代码基础上用 document.cookie 设置 token 的有效时长;上传重试等等护航功能。
关键是 ali-oss SDK 的 put 方法,它主要用来向 bucket 添加一个 object。
.put(name, file[,options])
- name String 类型的文件名
- {String|Buffer|ReadStream|File(only support Browser)|Blob(only support Browser)} object local path, content buffer or ReadStream content instance use in Node, Blob and html5 File
七牛云上传浅析
qiniu-js 是一个基于七牛 API 开发的前端 SDK。
import * as qiniu from 'qiniu-js';
const putExtra = {fname: '', params: {}, mimeType: null };// fname 文件原文件名,params 自定义变量,mimeType 数组,限制文件上传类型
const config = {
useCdnDomain: true, // 表示是否使用 cdn 加速域名
disableStatisticsReport: false, // 是否禁用日志报告,为布尔值,默认为 false
retryCount: 5, // 上传自动重试次数(整体重试次数,而不是某个分片的重试次数);默认 3 次(即上传失败后最多重试两次);目前仅在上传过程中产生 599 内部错误时生效,但是未来很可能会扩展为支持更多的情况
region: qiniu.region.z0, // 选择上传域名区域;当为 null 或 undefined 时,自动分析上传域名区域
};
const token = apiNode.generateOssToken({type}) // 调用 nodejs 端接口,根据类型生成某个 bucket 有效的 token
this.observable = qiniu.upload(obj.file, obj.fileName, token, putExtra, config);
this.observable.subscribe({next: (res) => {this.emit('progress', Math.ceil(res.total.percent));
},
error: (err) => {this.clear();
this.emit('error', err, obj.imgName);
},
complete: (res) => {this.clear();
this.emit('complete', res.hash, obj.imgName);
},
});
关键是 qiniu-js SDK 的 upload 方法,它是上传的核心。
qiniu.upload(file: blob, key: string, token: string, putExtra: object, config: object): observable
file: Blob 对象,上传的文件
key: 文件资源名
token: 上传验证信息,前端通过接口请求后端获得
config: 包括 cdn 加速,日志报告,上传区域名,上传自动重试次数,分片上传的请求并发数,MD5 校验等等
putExtra: 自定义的一些内容
其次就是这个上传成功后的返回的 observable 对象,在其中可以做一些上传后的处理。
这个方法本身是一个 observable 对象,有 subscribe 方法,主要有 next 和 error,complete 三个事件。
next: 接收上传进度信息,res 是一个带有 total 字段的 object,包含 loaded、total、percent 三个属性,提供上传进度信息。
error: 上传错误后触发;自动重试本身并不会触发该错误,而当重试次数到达上限后则可以触发。当不是 xhr 请求错误时,会把当前错误产生原因直接抛出,诸如 JSON 解析异常等;当产生 xhr 请求错误时,参数 err 为一个包含 code、message、isRequestError 三个属性的 object
complete: 接收上传完成后的后端返回信息,具体返回结构取决于后端 sdk 的配置
与自家服务端和 ali-oss 不同的是,qiniu-js 提供了除成功和失败额外的 next 事件,可以实时监测到上传的进度,用户感知上传进度是起码的用户体验。
几种上传服务的对比
- 除自家上传服务通过原有的 cookie 做验证以外,ali-oss 和 qiniu-js 都需要 token,而且出于安全的考虑,这个 token 必须从服务端获取,一般来说都有 nodejs,java,go,python 等等语言的服务端实现
- 上传的文件类型都支持 Blob 对象
- ali-oss 支持多类型,多平台,从文件类型方面来讲是个不错的选择
- qiniu-js 的上传提供了进度的 next 事件,别具一格
- 从功能复杂性来说和文档友好度来说,ali-oss 更胜一筹
- 抛开大公司自己有上传服务,小公司单纯从上传方面做技术选型的话,个人建议使用 ali-oss
思考
- 前后端分离的模式下,一个完整功能的实现,前端往往会强依赖后端,注意是强依赖
- 个人认为前后端分离的分离,最好是仅仅从项目架构上做分离,而不是将组织架构做前后端的分离
- 想使得自己获得更大成长空间的程序员,尽量不要限定自己是前端,后端,从全栈的角度去提升自己,没有机会就创造机会,没有时间就挤出时间
- 若是身处前后端协作的团队,享受前后端组织架构分工带来的利好,但也不能因此麻木,尽可能多的走出知识舒适区,不要害怕浪费时间,不要害怕工作中用不到,学习另一端会反哺你对当前端的理解