公司最近须要使用阿里OSS
上传文件,可是文件类型不固定,开始的想法是经过Java
写接口,若是文件过大由后端对文件进行分片处理并上传。在调研过程当中发现ali-oss
公共模块中提供了分片上传的方法,因此找个工做就交给了前端来作,以减轻后端的压力,因而笔者就开始了漫长的调研过程。javascript
初期考虑只是简单的实现就好,可是为了之后方便维护以及复用状况笔者考虑,使用class
进行封装处理。html
使用技术栈:前端
依赖:vue
测试用html
结构以下(使用了element-ui
)java
<div class="home"> <input type="file" multiple='true' @change="onFileChange" id="file"> <el-button @click="upload">普通上传</el-button> <el-button @click="multipartUpload">分片上传</el-button> <el-button @click="stop">中止上传</el-button> <el-button @click="resume">中断续传</el-button> <el-progress :percentage="percentage"></el-progress> </div>
前端直接对接阿里oss
须要使用ali-oss
公共包,执行一下命令vue-cli
npm install --save-dev ali-oss
建立文件DockingOSS.ts
typescript
class DockingOSS { }
因为依赖于ali-oss
,要考虑到ali-oss
在初始化时所须要的参数,封装类时所须要的参数,因为使用typescript
就不能再让参数随意填写,而是使用接口对参数进行规范化处理。npm
interface allOssInterface { region:string; // 地域节点,必填 accessKeyId:string; // 用户id,必填 accessKeySecret:string; // 访问密钥,必填 bucket:string; // bucket名称,qjdev-pred-voices path?:string; // 路径,默认为"",用户长传到指定文件夹 secure?:Boolean; // 指示OSS客户端使用 HTTPS:true HTTP:false parallel?:number; // 分片数量 partSize?:number; // 分片大小 defaultName?:Boolean; // 是否使用默认名称 length?:number; // 随机名称长度 };
对其进行初始化element-ui
class DockingOSS { // ali-oss 实例 private allOSS:any; private parallel:number; private partSize:number; private defaultName:Boolean; private path:string; private length:number; constructor(data:allOssInterface){ let {region, accessKeyId, accessKeySecret, bucket, secure = true, parallel = 3, partSize = 1024 * 1024, defaultName = false, path = "", length = 50} = data; this.partSize = partSize; this.parallel = parallel; this.defaultName = defaultName; this.path = path; this.length = length; // 实例化ali-oss this.allOSS = new AliOss({region,accessKeyId,accessKeySecret,bucket,secure}); } }
添加普通上传方法,处于考虑到开发者可能须要把文件上传到不一样的文件夹,以及会使用随机文件名称或者使用固定文件名称,定义了两个方法用来处理上传路径和文件名称。后端
class DockingOSS { /** * 普通上传 * file:文件对象 * _fileName:固定文件名称 */ public upload(file:File,_fileName:string = ""):Promise<any>{ let fileName = _fileName; (!fileName) && (fileName = this.getFileName(file)); const pathName = this.accessPath(fileName); return this.allOSS.put(pathName, file) } }
添加获取路径和随机名称方法
class DockingOSS { // 获取名称 private getFileName(file:File):string{ let {defaultName} = this; const fileName:string = file.name; if(defaultName) return fileName; return this.randomFileName(); } // 随机文件名称 private randomFileName():string{ const data = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]; let nums = ""; const {length} = this; for (let i = 0; i < length; i++) { const randomStr:string = (Math.random()*61).toString() const r:number = parseInt(randomStr, 10); nums += (data[r]).toString(); } return nums; } }
添加分片上传上传方法
class DockingOSS { /** * 分片上传 * file:文件对象 * _fileName:固定文件名称 * progress:分片上传进度回调函数 */ public multipartUpload(file:File, progress:Function,_fileName:string):Promise<void>{ const {parallel,partSize} = this; let fileName = _fileName; (!fileName) && (fileName = this.getFileName(file)); const pathName = this.accessPath(fileName); return this.allOSS.multipartUpload(pathName, file, { parallel, partSize, progress }) } }
添加停止上传方法
class DockingOSS { // 停止上传 public cancel():void{ this.allOSS.cancel(); } }
添加续传方法,因为在续传时须要接收一些参数,须要从中获取到停止上传的文件对象,使用interface
对参数进行规范化。
interface checkpointInterface { file:File; name:string; fileSize:number; partSize:number; uploadId:string; }; class DockingOSS { /** * 分片续传 * checkpoint:中断上传的文件 * progress:进度回调函数 */ public resume(checkpoint:checkpointInterface, progress:Function):Promise<void>{ const { uploadId, file } = checkpoint; const {parallel,partSize,path} = this; return this.allOSS.multipartUpload(uploadId, file, { parallel, partSize, progress, checkpoint }) } }
简易封装就完成了,虽然封装不算太完善可是仍是能够知足大部分项目需求的,在应用过程当中,可能会不少地方用到该类,能够在类中添加单例,已保证整个项目中只存在一个实例,减小对内存的占用(提升性能从小事开始作起)。
实战应用:
<template> <div class="home"> <input type="file" multiple='true' @change="onFileChange" id="file"> <el-button @click="upload">普通上传</el-button> <el-button @click="multipartUpload">分片上传</el-button> <el-button @click="stop">中止上传</el-button> <el-button @click="resume">中断续传</el-button> <el-progress :percentage="percentage"></el-progress> </div> </template> <script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; import AliOss from "ali-oss"; import DockingOSS from '@/assets/DockingOSS'; let dockOss = new DockingOSS({ // 地域节点 region:"*********", // id accessKeyId:"************", // 访问密钥 accessKeySecret:"***********", // bucket bucket:"***********", path:"***********", secure: true }) @Component export default class HelloWorld extends Vue { // 中断上传存储文件内容 private checkpoints:object = {}; private percentage:number = 0; private filesArr:File[] = []; // 文件更改 private onFileChange(e:Event):void{ const target = Reflect.get(e,"target"); const value = Reflect.get(target,"value"); const tmpcnt = value.lastIndexOf(".") const exname = value.substring(tmpcnt + 1) const oFile = document.getElementById("file") || document.createElement("input"); const files:File[] = Reflect.get(oFile,"files"); const fileList = Array.from(files); this.filesArr = fileList; } // 分片上传 private multipartUpload() { this.percentage = 0; this.filesArr.forEach((file:File) => { // 当中止上传时须要也会走向catch // 错误提示:{status: 0, name: "cancel"} // 须要特殊处理 dockOss.multipartUpload(file,this.onMultipartUploadProgress) }); } // 上传进度 // checkpoint:返回的文件对象 // 若是文件过小,小于分片大小的话,则不会checkpoint,为undefined // 说明文件直接上传成功了 private onMultipartUploadProgress(e:number,checkpoint:any){ Reflect.set(this.checkpoints,checkpoint.uploadId,checkpoint) this.percentage = Number((e * 100).toFixed(0)); if(e === 1){ Reflect.deleteProperty(this.checkpoints,checkpoint.uploadId); } } // 普通上传 private upload():void{ this.filesArr.forEach((file:File) => { dockOss.upload(file); }) } // 停止上传 private stop():void{ dockOss.cancel() } // 续传 private resume():void{ Object.values(this.checkpoints).forEach((checkpoint) => { dockOss.resume(checkpoint,this.onMultipartUploadProgress) }); } } </script>
对接阿里oss也是没那么困难的,仍是想记录一下,毕竟不用每次都须要翻阅资料了。
须要说明的一点,为何没有直接使用element-ui
的上传组件,为了节省oss空间,以及防止用户误传文件,因此使用这种方式在用户提交数据时,进行文件上传,这样会更好一些。
记录生活分享技术,共同进步共同成长。若是文章中由什么错误,请在评论出提出指正,我会及时作出修改。