惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
The GitHub Blog
The GitHub Blog
Vercel News
Vercel News
D
DataBreaches.Net
MongoDB | Blog
MongoDB | Blog
H
Help Net Security
小众软件
小众软件
美团技术团队
T
The Blog of Author Tim Ferriss
爱范儿
爱范儿
D
Docker
Martin Fowler
Martin Fowler
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
Blog — PlanetScale
Blog — PlanetScale
H
Hackread – Cybersecurity News, Data Breaches, AI and More
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
V2EX
S
SegmentFault 最新的问题
云风的 BLOG
云风的 BLOG
B
Blog
雷峰网
雷峰网
The Cloudflare Blog

博客园 - 青玄鸟

.NET 中优雅处理 Server-Sent Events 请求取消 Dapr 订阅者参数无法正确反序列化问题 .NET 代码整洁手册 Blazor项目通过docker和nginx部署为静态站点的步骤 Moq mock 方法返回null空指针异常 基于接口隔离原则的依赖注入实现 HttpClient with Stream HttpClient partial update HttpClient 基本使用 值对象的封装 只读集合类型属性实现 适配器模式 模板模式 最少知识原则 单例模式 抽象工厂 简单工厂、工厂方法、抽象工厂 工厂方法模式 使用 Visual Studio Code创建和执行T-SQL
vue3.0 + ts 实现上传工厂(oss与cos)
青玄鸟 · 2023-11-06 · via 博客园 - 青玄鸟

概述

将上传基类命名为MOS(Mine Object Storage)

mos.ts代码

import {MosType} from './mosConfig'
import {Loading} from '../loading'
import {type BinaryFile, type MosFile} from './fileUtil'
import type { PathTemplate } from './pathTemplate'
import axios, { type AxiosResponse } from 'axios'
import {loadUser} from '../user'
export interface MosSettings{
    cdn:string | null
    bucket:string
    endpoint:string
}

export interface UploadOptions{
    cover:boolean 
    progress?:Function
    failed?:Function
}

export abstract class Mos{
    settings : MosSettings
    finished : boolean = false
    loading : Loading
    pathTemplate:string 

    constructor(settings:MosSettings,template:PathTemplate){
        this.settings = settings
        this.loading = new Loading()
        this.pathTemplate = `${template.appId}/@ownerId/@fileName`
    }

    abstract init() : void

    abstract uploadFile(file:BinaryFile,options:UploadOptions,token:string) : Promise<MosFile|null>

    async upload(file:BinaryFile,options:UploadOptions) : Promise<MosFile|null> {
        const user=loadUser()
        this.pathTemplate=this.pathTemplate.replace('@ownerId',user?.userId.toString() ?? '0')
        return await this.uploadFile(file,options,user?.token ?? '')
    }

    async getCredential(token:string,bucket:string,type: MosType) : Promise<AxiosResponse<any, any>>{
        const params = { bucket: bucket, ossSource: type }
        const url = `https://api.mos.com/api/v1/Credential` // 接口会根据不同的ossSource参数值返回不同的数据结构,因此可以放在基类

        return axios.get(url, { params ,headers:{
            'Authorization':token,
            'Content-Type':'application/json'
        }})
    }
}

oss.ts代码

import axios from "axios";
import type { BinaryFile, MosFile } from "./fileUtil";
import { Mos, type UploadOptions } from "./mos";
import { MosType, type OssConfig } from "./mosConfig";
import type { OssCredential } from "./mosCredential";
import type { PathTemplate } from "./pathTemplate";

export class Oss extends Mos{
    policy : string = ''
    signature : string = ''
    accessKeyId:string
    host:string

    constructor(config:OssConfig,template:PathTemplate) {
        super({bucket:config.bucket,endpoint:config.endpoint,cdn:config.cdn},template)
        this.accessKeyId = config.accessKeyId
        this.host=`https://${config.bucket}.${config.endpoint}`
    }

    init() {
        return
    }

    async getSTSToken(token:string){
        if (this.policy) return Promise.resolve()
        
        return await this.getCredential(token,this.settings.bucket,MosType.Oss).then(result => {
            const data = result.data.data as OssCredential;
            this.policy = data.policy;
            this.signature = data.signature;
        })
    }

    async uploadFile(file: BinaryFile, options: UploadOptions,token:string): Promise<MosFile|null> {
        this.finished = false
        this.loading.startLoading()
        await this.getSTSToken(token)
        const fileName = file.name
        const ossPrefix = 'TempFiles/'
        const saveKey = ossPrefix + this.pathTemplate.replace('@fileName',fileName)

        const formData = new FormData();
        formData.append("policy", this.policy);
        formData.append("Signature", this.signature);
        formData.append("bucket", this.settings.bucket);
        formData.append("key", saveKey);
        formData.append("OSSAccessKeyId", this.accessKeyId);
        formData.append("success_action_status", "201");

        formData.append("Content-Disposition", "attachment;filename=" + fileName + ";filename*=UTF-8''" + fileName);
        formData.append("file", file.binary);

        return axios.post(this.host, formData, {
            responseType: 'document',
            onUploadProgress: evt => options.progress && options.progress(evt),
        }).then(result => {
            this.finished = true;
            this.loading.endLoading();

            console.log("上传成功");

            const data = result.data;
            const url = data.getElementsByTagName("Location")[0].childNodes[0].nodeValue;

            return url ? Promise.resolve({ url,  fileName }) : Promise.reject(result);
        }).catch(error => {
            this.finished = true;
            this.loading.endLoading()

            console.log("上传失败");

            options.failed &&options.failed(error);

            return null
        })
    }    

}

cos.ts 代码

import type { BinaryFile, MosFile } from './fileUtil'
import { Mos, type UploadOptions } from './mos'
import { MosType, type CosConfig } from './mosConfig'
import type { PathTemplate } from './pathTemplate'
import COS from 'cos-js-sdk-v5'
import type { CosCredential } from './mosCredential'

export class TencentCOS extends Mos {
  cos: COS | null = null
  currentToken:string = ''

  constructor(config: CosConfig, template: PathTemplate) {
    super({ bucket: config.bucket, endpoint: config.endpoint, cdn: config.cdn }, template)
  }

  init() {
    this.cos = new COS({
      getAuthorization:  (options, callback) => this.getAuthorizationCredential(callback),
      UploadCheckContentMd5: true 
    })
  }
  async uploadFile(file: BinaryFile, options: UploadOptions, token: string): Promise<MosFile | null> {
    this.currentToken = token
    this.finished = false;
    this.loading.startLoading()
    const fileName = file.name
    const savekey = this.pathTemplate.replace('@fileName',fileName)
    return new Promise((resolve, reject) => {
        this.cos!.uploadFile({
            Bucket: this.settings.bucket,
            Region: this.settings.endpoint,
            Key: savekey,
            Body: file.binary,
            SliceSize: 1024 * 1024 * 5, 
            Headers: {
                'content-disposition': "attachment;filename=" + fileName + ";filename*=UTF-8''" + fileName
            },
            onProgress: evt => options.progress && options.progress(evt),
        }, (err, data) => {
            this.finished = true;
            this.loading.endLoading()

            if (err) {
                console.log("上传失败");
                options.failed && options.failed(err);
                reject(err);
            }
            if (data) {
                console.log("上传成功");
                resolve({ url, fileName });
            }
        });
    })
  }

  getAuthorizationCredential(callback:Function){
      this.getCredential(this.currentToken, this.settings.bucket, MosType.Cos).then((result) => {
      const data = result.data.data as CosCredential
      callback({
        TmpSecretId: data.tmpSecretId,
          TmpSecretKey: data.tmpSecretKey,
          SecurityToken: data.token,
          StartTime: data.startTime,
          ExpiredTime: data.expiredTime
      })
    })
  }

}

mosFactory.ts 代码

import { TencentCOS as Cos } from "./cos";
import type { Mos } from "./mos";
import {MosConfig, MosType } from "./mosConfig";
import { Oss } from "./oss";
import type { PathTemplate } from "./pathTemplate";

export function CreateMos (template:PathTemplate,type:MosType = MosType.Cos) : Mos {
    const config:MosConfig = new MosConfig()
    let mos:Mos 
    switch (type) {
        case MosType.Cos:
            mos= new Cos(config.cos,template)
            break;
        case MosType.Oss:
            mos= new Oss(config.oss,template)
            break
        default:
            mos= new Cos(config.cos,template)
            break;
    }
    mos.init()

    return mos
}

export function CreateDefaultMos(type:MosType = MosType.Cos) : Mos {
    return CreateMos({appId:'demo'},type)
}

其他代码

PathTemplate.ts

export interface PathTemplate{
    appId:string //应用Id 
}

mossCredential.ts

export interface OssCredential{
    policy:string
    signature:string
    accessKeyId:string|null
}

export interface CosCredential{
    tmpSecretId:string
    tmpSecretKey:string
    token:string
    startTime:number
    expiredTime:number
}

fileUtil.ts

export interface MosFile {
  url: string | null
  fileName: string
}

export interface BinaryFile{
  binary: Blob
  name:string
}

mosConfig.ts

export interface OssConfig {
  isTemp: boolean
  bucket: string
  endpoint: string
  cdn: string
  accessKeyId: string
}
export interface CosConfig{
    bucket: string
    endpoint: string
    cdn: string
}

export class MosConfig {
  oss: OssConfig = {
    bucket: 'demo-bucket',
    endpoint: 'oss-cn.aliyuncs.com',
    cdn: '',
    accessKeyId: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  }
  cos:CosConfig = {
    bucket: 'demo-bucket',
    endpoint: 'beijing',
    cdn: ""
  }
}

export enum MosType{
    Oss = 1,
    Cos
}

在vue3.0是使用

在App.vue中

import {provide} from 'vue'
import {CreateDefaultMos} from './assets/ts/mos/mosFactory'

provide('mos',CreateDefaultMos())

在组件中注入使用

import {inject } from 'vue'

const mos:Mos = inject('mos')!