feat: 增加 image 文件上传(前端直传) 100%

This commit is contained in:
YunaiV
2025-04-18 18:30:50 +08:00
parent 1bacb6759f
commit 87c6074e19
7 changed files with 115 additions and 22 deletions

View File

@@ -12,8 +12,7 @@ import { ref, toRefs, watch } from 'vue';
import { isFunction, isObject, isString } from '@vben/utils';
import { checkFileType } from './helper';
import { UploadResultStatus } from './typing';
import { useUploadType } from './use-upload';
import { uploadFile } from '#/api/infra/file';
import { useUpload, useUploadType } from './use-upload';
defineOptions({ name: 'FileUpload', inheritAttrs: false });
@@ -22,7 +21,7 @@ const props = withDefaults(
// 根据后缀,或者其他
accept?: string[];
api?: (
file: Blob | File,
file: File,
onUploadProgress?: AxiosProgressEvent,
) => Promise<AxiosResponse<any>>;
disabled?: boolean;
@@ -47,10 +46,7 @@ const props = withDefaults(
maxNumber: 1,
accept: () => [],
multiple: false,
api: (file: Blob | File, onUploadProgress?: AxiosProgressEvent) => {
// TODO @芋艿:处理上传;前端上传
return uploadFile({ file }, onUploadProgress);
},
api: useUpload().httpRequest,
resultField: '',
showDescription: false,
},
@@ -171,7 +167,6 @@ function getValue() {
const list = (fileList.value || [])
.filter((item) => item?.status === UploadResultStatus.DONE)
.map((item: any) => {
debugger
if (item?.response && props?.resultField) {
return item?.response;
}

View File

@@ -12,8 +12,7 @@ import { ref, toRefs, watch } from 'vue';
import { isFunction, isObject, isString } from '@vben/utils';
import { checkImgType, defaultImageAccepts } from './helper';
import { UploadResultStatus } from './typing';
import { useUploadType } from './use-upload';
import { uploadFile } from '#/api/infra/file';
import { useUpload, useUploadType } from './use-upload';
defineOptions({ name: 'ImageUpload', inheritAttrs: false });
@@ -22,7 +21,7 @@ const props = withDefaults(
// 根据后缀,或者其他
accept?: string[];
api?: (
file: Blob | File,
file: File,
onUploadProgress?: AxiosProgressEvent,
) => Promise<AxiosResponse<any>>;
disabled?: boolean;
@@ -49,11 +48,7 @@ const props = withDefaults(
maxNumber: 1,
accept: () => defaultImageAccepts,
multiple: false,
api: (file: Blob | File, onUploadProgress?: AxiosProgressEvent) => {
// TODO @芋艿:处理上传;前端上传
debugger
return uploadFile({ file }, onUploadProgress);
},
api: useUpload().httpRequest,
resultField: '',
showDescription: true,
},
@@ -207,7 +202,6 @@ function getValue() {
const list = (fileList.value || [])
.filter((item) => item?.status === UploadResultStatus.DONE)
.map((item: any) => {
debugger
if (item?.response && props?.resultField) {
return item?.response;
}

View File

@@ -1,8 +1,11 @@
import type { Ref } from 'vue';
import type { AxiosProgressEvent, InfraFileApi } from '#/api/infra/file';
import { computed, unref } from 'vue';
import { $t } from '@vben/locales';
import CryptoJS from 'crypto-js'
import axios from 'axios'
import { uploadFile, getFilePresignedUrl, createFile } from '#/api/infra/file';
export function useUploadType({
acceptRef,
@@ -59,3 +62,95 @@ export function useUploadType({
});
return { getAccept, getStringAccept, getHelpText };
}
// TODO @芋艿:目前保持和 admin-vue3 一致,后续可能重构
export const useUpload = () => {
// 后端上传地址
const uploadUrl = getUploadUrl()
// 是否使用前端直连上传
const isClientUpload = UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE
// 重写ElUpload上传方法
const httpRequest = async (file: File, onUploadProgress?: AxiosProgressEvent) => {
// 模式一:前端上传
if (isClientUpload) {
// 1.1 生成文件名称
const fileName = await generateFileName(file)
// 1.2 获取文件预签名地址
const presignedInfo = await getFilePresignedUrl(fileName)
// 1.3 上传文件
return axios
.put(presignedInfo.uploadUrl, file, {
headers: {
'Content-Type': file.type
}
})
.then(() => {
// 1.4. 记录文件信息到后端(异步)
createFile0(presignedInfo, fileName, file)
// 通知成功,数据格式保持与后端上传的返回结果一致
return { data: presignedInfo.url }
})
} else {
// 模式二:后端上传
return uploadFile({ file }, onUploadProgress);
}
}
return {
uploadUrl,
httpRequest
}
}
/**
* 获得上传 URL
*/
export const getUploadUrl = (): string => {
return import.meta.env.VITE_BASE_URL + import.meta.env.VITE_GLOB_API_URL + '/infra/file/upload'
}
/**
* 创建文件信息
*
* @param vo 文件预签名信息
* @param name 文件名称
* @param file 文件
*/
function createFile0(vo: InfraFileApi.FilePresignedUrlRespVO, name: string, file: File) {
const fileVO = {
configId: vo.configId,
url: vo.url,
path: name,
name: file.name,
type: file.type,
size: file.size
}
createFile(fileVO)
return fileVO
}
/**
* 生成文件名称使用算法SHA256
*
* @param file 要上传的文件
*/
async function generateFileName(file: File) {
// 读取文件内容
const data = await file.arrayBuffer()
const wordArray = CryptoJS.lib.WordArray.create(data)
// 计算SHA256
const sha256 = CryptoJS.SHA256(wordArray).toString()
// 拼接后缀
const ext = file.name.substring(file.name.lastIndexOf('.'))
return `${sha256}${ext}`
}
/**
* 上传类型
*/
enum UPLOAD_TYPE {
// 客户端直接上传只支持S3服务
CLIENT = 'client',
// 客户端发送到后端上传
SERVER = 'server'
}