前端對接阿里OSS

公司最近須要使用阿里OSS上傳文件,可是文件類型不固定,開始的想法是經過Java寫接口,若是文件過大由後端對文件進行分片處理並上傳。在調研過程當中發現ali-oss公共模塊中提供了分片上傳的方法,因此找個工做就交給了前端來作,以減輕後端的壓力,因而筆者就開始了漫長的調研過程。javascript

初期考慮只是簡單的實現就好,可是爲了之後方便維護以及複用狀況筆者考慮,使用class進行封裝處理。html

使用技術棧:前端

  1. vue-cli 3.0
  2. typescript

依賴:vue

  1. ali-oss
  2. Element-ui(可忽略)

測試用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.tstypescript

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空間,以及防止用戶誤傳文件,因此使用這種方式在用戶提交數據時,進行文件上傳,這樣會更好一些。

記錄生活分享技術,共同進步共同成長。若是文章中由什麼錯誤,請在評論出提出指正,我會及時作出修改。

相關文章
相關標籤/搜索