vue3 + ts 直接上传图片到OSS

前言:本文使用 服务端生成PutObject所需的签名URL, 前端凭借签名URL,不依赖oss sdk 直接上传文件。

之前项目都是前端上传文件到服务器,然后由服务器上传到oss,这种情况呢,数据要在网络上传输两次,会导致网络资源浪费,增大服务器的开销,本次采用前端直传的方式来实现图片上传。

原理:
在这里插入图片描述

参考:阿里云文档

代码实现

<template>
  <!-- 上传图片组件 -->
  <el-upload
    v-model="imgUrl"
    class="single-uploader"
    :show-file-list="false"
    list-type="picture-card"
    :before-upload="handleBeforeUpload"
    :http-request="uploadFile"
  >
    <img v-if="imgUrl" :src="imgUrl" class="single-uploader__image" />
    <el-icon v-if="!progress.showProgress && !imgUrl" class="single-uploader__icon"><Plus /></el-icon>
    <el-progress v-if="progress.showProgress" type="circle" :percentage="progress.upProgress" />
  </el-upload>
</template>

<script setup lang="ts">
import { UploadRawFile, UploadRequestOptions, ElMessage } from "element-plus";
import { reactive, ref } from "vue";
import { uploadFileApi, ossUploadApi } from "@/api/file/index";
import { RuleFileForm } from "@/api/file/types"; 

const imgUrl = ref<string>();
const uploadFileInfo = reactive<RuleFileForm>({
  filename: '',
  prefix: ''
})

// 定义上传文件进度条
const progress = reactive({
  upProgress: 0,
  showProgress: false,
});

/**
 * 自定义图片上传
 */
async function uploadFile(options: UploadRequestOptions): Promise<any> {
  uploadFileInfo.filename = options.file.name;
  uploadFileInfo.prefix = 'user/logo';
  progress.showProgress = true;

  uploadFileApi(uploadFileInfo).then((res)=>{
    if(res.code == 0){
       const { accessUrl, uploadSignedUrl } = res.data; 
	   ossUploadApi(uploadSignedUrl, options.file, (progressEvent: any) => {
     	 progress.upProgress = (progressEvent.loaded / progressEvent.total * 100) | 0  // 上传进度条设置
	   }).then(r => {
	     console.log(r);
	     imgUrl.value = accessUrl;
	     progress.showProgress = false;
	   })
    }  
  })
}

/**
 * 限制用户上传文件的格式和大小
 */
function handleBeforeUpload(file: UploadRawFile) {
  if (file.size > 2 * 1048 * 1048) {
    ElMessage.warning("上传图片不能大于2M");
    return false;
  }
  return true;
}
</script>


import request from "@/utils/request";

import { FileInfo, FileInfoRes } from "./types";

/**上传文件 */
export function uploadFileApi(uploadFileInfo: FileInfo){
  return request<FileInfoRes>({
    url: "/api/file/get-upload-url",
    method: "post",
    data: uploadFileInfo,
  });
}


export function ossUploadApi(uploadSignedUrl: string, file: any, progressEvent: any) {
  return request({
    url: uploadSignedUrl,
    method: 'put',
    headers: {
      "Content-Type": ""   // 必须设置为空,不然会报403错误
    },
    data: file,
    onUploadProgress: progressEvent,
  })
}

在这里插入图片描述

上传成功!