公司最近須要使用阿里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空間,以及防止用戶誤傳文件,因此使用這種方式在用戶提交數據時,進行文件上傳,這樣會更好一些。
記錄生活分享技術,共同進步共同成長。若是文章中由什麼錯誤,請在評論出提出指正,我會及時作出修改。