Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

浅析前端上传 #152

Open
FrankKai opened this issue Jul 12, 2019 · 7 comments
Open

浅析前端上传 #152

FrankKai opened this issue Jul 12, 2019 · 7 comments

Comments

@FrankKai
Copy link
Owner

FrankKai commented Jul 12, 2019

图片,音频,视频等等这几种常见的资源类型,如果需要从前端上传到服务端,有几种方式呢?不妨回顾一下经历过的项目想一想。

项目上也用到很多上传文件的地方,七牛云,阿里云OSS,讯飞web api上传都接触过,所以在这里做一个记录,总结一下前端上传的几种方式。

  • 上传基本概念
  • 常见前端上传场景
  • 自家后端服务上传浅析
  • 阿里云OSS上传浅析
  • 七牛云上传浅析
  • 几种上传服务的对比
  • 思考
@FrankKai FrankKai changed the title 前端概念里的字节数组是什么? 前端上传总结 Jul 13, 2019
@FrankKai
Copy link
Owner Author

FrankKai commented Jul 13, 2019

上传基本概念

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那些事儿

@FrankKai
Copy link
Owner Author

FrankKai commented Jul 14, 2019

常见前端上传场景

  • 原生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对象。

@FrankKai
Copy link
Owner Author

FrankKai commented Jul 14, 2019

自家后端上传服务浅析

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(()=>{});

@FrankKai
Copy link
Owner Author

FrankKai commented Jul 14, 2019

阿里云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

@FrankKai
Copy link
Owner Author

FrankKai commented Jul 19, 2019

七牛云上传浅析

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事件,可以实时监测到上传的进度,用户感知上传进度是起码的用户体验。

@FrankKai
Copy link
Owner Author

FrankKai commented Jul 19, 2019

几种上传服务的对比

  • 除自家上传服务通过原有的cookie做验证以外,ali-oss和qiniu-js都需要token,而且出于安全的考虑,这个token必须从服务端获取,一般来说都有nodejs,java,go,python等等语言的服务端实现
  • 上传的文件类型都支持Blob对象
  • ali-oss支持多类型,多平台,从文件类型方面来讲是个不错的选择
  • qiniu-js的上传提供了进度的next事件,别具一格
  • 从功能复杂性来说和文档友好度来说,ali-oss更胜一筹
  • 抛开大公司自己有上传服务,小公司单纯从上传方面做技术选型的话,个人建议使用ali-oss

@FrankKai FrankKai changed the title 前端上传总结 浅析前端上传 Jul 19, 2019
@FrankKai
Copy link
Owner Author

思考

  • 前后端分离的模式下,一个完整功能的实现,前端往往会强依赖后端,注意是强依赖
  • 个人认为前后端分离的分离,最好是仅仅从项目架构上做分离,而不是将组织架构做前后端的分离
  • 想使得自己获得更大成长空间的程序员,尽量不要限定自己是前端,后端,从全栈的角度去提升自己,没有机会就创造机会,没有时间就挤出时间
  • 若是身处前后端协作的团队,享受前后端组织架构分工带来的利好,但也不能因此麻木,尽可能多的走出知识舒适区,不要害怕浪费时间,不要害怕工作中用不到,学习另一端会反哺你对当前端的理解

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant