vue3+Ts+element-plus+Upload+ Aliyun OSS front-end direct transmission component secondary packaging

Recently, uploading files in the project needs to be uploaded with Alibaba Cloud's OSS, and I have never touched it before. At the beginning, the project leader forwarded a link about [Front-end OSS Direct Transfer] https://help.aliyun.com/document_detail/31926.html to me, so I started to study using the simplest method: introducing an ali-oss npm package

npm install ali-oss -S

Aliyun's document link about the first simple method: https://help.aliyun.com/document_detail/120092.htm?spm=a2c4g.11186623.0.0.74366fedJb1Pmx#section-iy3-bfe-7mn

I didn't look too carefully at the beginning, until the backend was ready and I started to do it, I found that this method did not match the element-plus upload component used in my project. Since the secondary packaging of upload has been done in advance, I don't want to change it, so I just check online to see if there are other methods. Hard work pays off, I finally found it: https://blog.csdn.net/qq_34761385/article/details/121352962 , the code is as follows:

<template>
	<div class="uploadimg">
		<el-upload
		  :action="url"
		  list-type="picture-card"
		  accept="image/*"
		  :data="basedata"
		  :file-list="fileList"
		  :limit="limit"
		  :auto-upload="true"
		  :on-preview="handlePictureCardPreview"
		  :on-success="handleSuccess"
		  :before-upload="handleUpload"
		  :on-exceed="handleExceed"
		  :on-remove="handleRemove">
		  <i class="el-icon-plus"></i>
		</el-upload>
		<el-dialog :visible.sync="dialogVisible" append-to-body>
		  <img width="100%" :src="dialogImageUrl" alt="">
		</el-dialog>
	</div>
</template>
<script>
  import { filePolicy } from "@/api/upload"
  export default {
	name:'Uploadimg',
	props:{
		limit:Number,
		filelist:Array(),
	},
    data() {
      return {
		url:'',
		fileList:[],
		// filelist:[],  //上传文件url列表
		basedata:{},
        dialogImageUrl: '',
        dialogVisible: false 
      };
    },
	mounted() {
		this.fileList=this.filelist.map(item=>{return {url:item}})
	},
    methods: {
	  handleUpload(file){
		  console.log("开始上传:",file);
		  const that=this
		  return new Promise((resolve, reject) => {
			  filePolicy().then(key=>{
						that.url=key.data.data.host;
			  			var name=file.name;
						key.data.data.OSSAccessKeyId=key.data.data.accessid;
						key.data.data.key=key.data.data.dir+'/'+file.uid+name;
						delete key.data.data.callback
			  			that.basedata=key.data.data;
						var img=key.data.data.cdnHost+'/'+key.data.data.key;
						that.filelist.push(img);
						resolve();
			  }).catch(err => {reject(err);})
		  })
	  },
	  handleSuccess(response, file, fileList){
		  console.log("上传成功:",response,file, fileList);
		  this.fileList=fileList;
		  this.$emit("getfile",this.filelist);
	  },
	  handleExceed(files, fileList) {
	          this.$message.warning(`当前限制选择 ${this.limit} 个文件`);
	        },
      handleRemove(file, fileList) {
		console.log("删除文件:",file);
		this.fileList=fileList;
		var f = this.filelist.find(item=>item.indexOf(file.uid)!=-1);
		var i = this.filelist.indexOf(f);
		this.filelist.splice(i,1);
		this.$emit("getfile",this.filelist);
      },
      handlePictureCardPreview(file) {
		console.log("文件预览:",file);
        this.dialogImageUrl = file.url;
        this.dialogVisible = true;
      }
    },
	
  }
</script>

The filePolicy here is a promise asynchronous request, which mainly obtains the upload key of Alibaba Cloud. code show as below:

import request from '@/request/request'
 
export function filePolicy() {
	return new Promise((resolve, reject)=>{
		 request.get('/user/file/policy',{}).then(res=>{
			 console.log("秘钥:",res.data);
			 resolve(res);
		 }).catch(err=>{console.log("网络错误",err);});
	})
}
 

 I copied the above code directly, and encountered the following problems in local testing:

  1. When uploading a file, sometimes the url called by the post method is the local project service address (local front-end service address: localhost:3000). The reason for this is that there is no response result when requesting the filePolicy interface. The [action] attribute value of the upload component has not been obtained, but no corresponding processing has been done in the before-upload hook function. The upload component is already executing the upload (post) operation. See the code for the solution
  2. After solving the first problem, the upload was finally successful, but OSS Alibaba Cloud did not return the address of the uploaded file, and the Alibaba Cloud service did not respond;
  3.  Finally, by checking the information, I found out that the image address is spliced ​​with fixed variables. Since we have permission settings, we have to add `${OSS_FLAG.host}/${basedata.value.key}?OSSAccessKeyId=${basedata.value.OSSAccessKeyId}&Expires=${Expires.value}&Signature=${basedata.value.signature}` after the address. After adding it, due to the time limit, the image will report a 403 error. The solution: The upload is successfully processed in the on-success hook function, and the image address is changed to a local address

 

Below is my complete code as follows:

upload component:

<template>
  <el-upload
    v-model:file-list="fileLists"
    :action="actionUrl"
    :limit="limit"
    :disabled="disabled"
    list-type="picture-card"
    :auto-upload="true"
    :multiple="false"
    :data="basedata"
    :accept="accept"
    :on-preview="handlePictureCardPreview"
    :on-remove="handleRemove"
    :before-upload="beforeUpload"
    :on-success="upSuccess"
    :class="{ limitOpctin: limit === fileLists.length }"
  >
    <template #tip>
      <div class="tips">
        {
   
   { tips }}
      </div>
    </template>
    <el-icon>
      <Plus />
    </el-icon>
  </el-upload>

  <el-dialog v-model="dialogVisible">
    <img
      style="width: 100%"
      w-full
      :src="dialogImageUrl"
      alt="图片预览"
    />
  </el-dialog>
</template>
<script setup lang="ts">
/*
example: <UpLoad :limit='1' v-model:fileLists='ruleForm.fileLists' accept='image/png,image/jpg'/>
*/
import { filePolicy } from '~/api'
import type { UploadProps, UploadUserFile, UploadRawFile ,UploadFile,UploadFiles} from "element-plus";
import { OSS_FLAG } from '~/utils/const'
import { AxiosResponse } from "axios";
import { RESULT } from "~/api/types";
import { Auth, BaseData } from './index'

interface Props {
  limit: number;
  accept: string;
  tips: string;
  fileLists: UploadUserFile[];
  disabled: boolean;
  sizeMax: number,
  fileType: string
}


const props = withDefaults(defineProps<Props>(), {
  limit: 1,
  accept: "image/png,image/jpeg,image/gif,image/jpg",
  tips: "",
  fileLists: () => [],
  disabled: false,
  sizeMax: 5,
  fileType: 'img'
});


const emit = defineEmits(["update:fileLists"]);

const infoList = ref([]);
const dialogImageUrl = ref("");
const fileName = ref('')
const Expires = ref('')
const percent = ref(0);
// const actionUrl = "api/upload";
const actionUrl = OSS_FLAG.host
const dialogVisible = ref(false);
let basedata = ref<BaseData>(null)


onMounted(() => {
  infoList.value = props.fileLists || [];
});

const handleRemove = (uploadFile) => {
  infoList.value.forEach((v, i) => {
    if (v.uid == uploadFile.uid) {
      infoList.value.splice(i, 1);
    }
  });
  emit("update:fileLists", infoList.value);
};

const handlePictureCardPreview = (uploadFile) => {
  dialogImageUrl.value = uploadFile.url;
  dialogVisible.value = true;
};


const upSuccess = (res:any, file:UploadFile,uploadFiles:UploadFiles) => {
  // console.log('success>>>>',res);
  // console.log('success>>>>',file.url);
  // console.log('success>>>>',uploadFiles);
  
  infoList.value = [
    ...infoList.value,
    {
      // url: `${OSS_FLAG.host}/${basedata.value.key}?OSSAccessKeyId=${basedata.value.OSSAccessKeyId}&Expires=${Expires.value}&Signature=${basedata.value.signature}`,
      url:file.url,//由于图片会过期,所以url使用本地图片路径
      name: fileName.value,
      fileName:basedata.value.key
      // url: 'https://snc-ai-prod-oss.oss-cn-hangzhou.aliyuncs.com/gamehub/1663662037237avatar.png?OSSAccessKeyId=LTAI5tHB6AeMcUBz85fxHjm6&Expires=1663671680&Signature=v%2FVJDq4F7HcDzcNqiV6NfxtnBmg%3D'
    }
  ]
  emit("update:fileLists", infoList.value);
}


const beforeUpload = async (rawFile: UploadRawFile) => {
  const { size, name, uid } = rawFile;
  console.log('UploadRawFile>>>>>>', rawFile);

  if (size / 1024 / 1024 > props.sizeMax) {
    ElMessage.error(`文件大小超过了 ${props.sizeMax}MB!`)
    return false
  }
//以下处理一定要加,不然会出现问题1
  try {
    const result: any = await filePolicy()
    const data: Auth = result.data
    fileName.value = rawFile.name
    Expires.value = data.expire
    basedata.value = {
      key: data.dir + props.fileType + '/' + rawFile.uid + '_' + rawFile.name,
      success_action_status: '200',
      policy: data.policy,
      OSSAccessKeyId: data.accessId,
      signature: data.signature
    }
    return true
  } catch (err) {
    return false
  }
}

</script>
<style lang="scss" scoped>
.limitOpctin :deep(.ep-upload--picture-card) {
  display: none !important;
}

:deep(.ep-upload-list__item .ep-icon--close-tip) {
  display: none !important;
}

.tips {
  color: #999 !important;
  font-size: 12px;
}
</style>

parent component:

<el-form
    ref="ruleFormRef"
    :model="ruleForm"
    :rules="rules"
    label-width="140px"
    class="ruleForm-add"
 >
 <el-form-item
    label="icon图标:"
     prop="gameIco"
 >
          <Upload
            v-model:fileLists="ruleForm.gameIco"
            :tips="TIPS.icon"
          />
   </el-form-item>
</el-form-item>

The return parameters of the backend filePolicy interface are as follows

 

Method 2 Alibaba Cloud document address: https://help.aliyun.com/document_detail/31926.html

Guess you like

Origin blog.csdn.net/qq_38902432/article/details/127070648