reactor:【INFRA】文件上传 api,增加 directory 参数,去除 path 参数,并支持按照日期分目录、文件名不再使用 sha256 而是时间戳
							parent
							
								
									368f7c753f
								
							
						
					
					
						commit
						91d70b41cb
					
				|  | @ -259,6 +259,7 @@ setupVbenVxeTable({ | |||
| 
 | ||||
|     // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
 | ||||
|     // vxeUI.formats.add
 | ||||
|     // add by 星语:数量格式化,例如说:金额
 | ||||
|     vxeUI.formats.add('formatAmount', { | ||||
|       cellFormatMethod({ cellValue }, digits = 2) { | ||||
|         if (cellValue === null || cellValue === undefined) { | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ export namespace BpmCategoryApi { | |||
|   } | ||||
| 
 | ||||
|   /** 模型分类信息 */ | ||||
|   // TODO @jason:这个应该非 api 的,可以考虑抽到页面里哈。
 | ||||
|   export interface ModelCategoryInfo { | ||||
|     id: number; | ||||
|     name: string; | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ import { requestClient } from '#/api/request'; | |||
| 
 | ||||
| export namespace BpmModelApi { | ||||
|   /** 用户信息 TODO 这个是不是可以抽取出来定义在公共模块 */ | ||||
|   // TODO @芋艿:一起看看。
 | ||||
|   export interface UserInfo { | ||||
|     id: number; | ||||
|     nickname: string; | ||||
|  | @ -9,6 +10,7 @@ export namespace BpmModelApi { | |||
|     deptId?: number; | ||||
|     deptName?: string; | ||||
|   } | ||||
| 
 | ||||
|   /** 流程定义 VO */ | ||||
|   export interface ProcessDefinitionVO { | ||||
|     id: string; | ||||
|  |  | |||
|  | @ -23,12 +23,13 @@ export namespace InfraFileApi { | |||
|     configId: number; // 文件配置编号
 | ||||
|     uploadUrl: string; // 文件上传 URL
 | ||||
|     url: string; // 文件 URL
 | ||||
|     path: string; // 文件路径
 | ||||
|   } | ||||
| 
 | ||||
|   /** 上传文件 */ | ||||
|   export interface FileUploadReqVO { | ||||
|     file: globalThis.File; | ||||
|     path?: string; | ||||
|     directory?: string; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | @ -45,11 +46,11 @@ export function deleteFile(id: number) { | |||
| } | ||||
| 
 | ||||
| /** 获取文件预签名地址 */ | ||||
| export function getFilePresignedUrl(path: string) { | ||||
| export function getFilePresignedUrl(name: string, directory?: string) { | ||||
|   return requestClient.get<InfraFileApi.FilePresignedUrlRespVO>( | ||||
|     '/infra/file/presigned-url', | ||||
|     { | ||||
|       params: { path }, | ||||
|       params: { name, directory }, | ||||
|     }, | ||||
|   ); | ||||
| } | ||||
|  | @ -64,5 +65,9 @@ export function uploadFile( | |||
|   data: InfraFileApi.FileUploadReqVO, | ||||
|   onUploadProgress?: AxiosProgressEvent, | ||||
| ) { | ||||
|   // 特殊:由于 upload 内部封装,即使 directory 为 undefined,也会传递给后端
 | ||||
|   if (!data.directory) { | ||||
|     delete data.directory; | ||||
|   } | ||||
|   return requestClient.upload('/infra/file/upload', data, { onUploadProgress }); | ||||
| } | ||||
|  |  | |||
|  | @ -28,6 +28,8 @@ const props = withDefaults( | |||
|       file: File, | ||||
|       onUploadProgress?: AxiosProgressEvent, | ||||
|     ) => Promise<AxiosResponse<any>>; | ||||
|     // 上传的目录 | ||||
|     directory?: string; | ||||
|     disabled?: boolean; | ||||
|     helpText?: string; | ||||
|     // 最大数量的文件,Infinity不限制 | ||||
|  | @ -44,13 +46,14 @@ const props = withDefaults( | |||
|   }>(), | ||||
|   { | ||||
|     value: () => [], | ||||
|     directory: undefined, | ||||
|     disabled: false, | ||||
|     helpText: '', | ||||
|     maxSize: 2, | ||||
|     maxNumber: 1, | ||||
|     accept: () => [], | ||||
|     multiple: false, | ||||
|     api: useUpload().httpRequest, | ||||
|     api: undefined, | ||||
|     resultField: '', | ||||
|     showDescription: false, | ||||
|   }, | ||||
|  | @ -141,10 +144,9 @@ const beforeUpload = async (file: File) => { | |||
| }; | ||||
| 
 | ||||
| async function customRequest(info: UploadRequestOption<any>) { | ||||
|   const { api } = props; | ||||
|   let { api } = props; | ||||
|   if (!api || !isFunction(api)) { | ||||
|     console.warn('upload api must exist and be a function'); | ||||
|     return; | ||||
|     api = useUpload(props.directory).httpRequest; | ||||
|   } | ||||
|   try { | ||||
|     // 上传文件 | ||||
|  |  | |||
|  | @ -30,6 +30,8 @@ const props = withDefaults( | |||
|       file: File, | ||||
|       onUploadProgress?: AxiosProgressEvent, | ||||
|     ) => Promise<AxiosResponse<any>>; | ||||
|     // 上传的目录 | ||||
|     directory?: string; | ||||
|     disabled?: boolean; | ||||
|     helpText?: string; | ||||
|     listType?: UploadListType; | ||||
|  | @ -47,6 +49,7 @@ const props = withDefaults( | |||
|   }>(), | ||||
|   { | ||||
|     value: () => [], | ||||
|     directory: undefined, | ||||
|     disabled: false, | ||||
|     listType: 'picture-card', | ||||
|     helpText: '', | ||||
|  | @ -54,7 +57,7 @@ const props = withDefaults( | |||
|     maxNumber: 1, | ||||
|     accept: () => defaultImageAccepts, | ||||
|     multiple: false, | ||||
|     api: useUpload().httpRequest, | ||||
|     api: undefined, | ||||
|     resultField: '', | ||||
|     showDescription: true, | ||||
|   }, | ||||
|  | @ -177,10 +180,9 @@ const beforeUpload = async (file: File) => { | |||
| }; | ||||
| 
 | ||||
| async function customRequest(info: UploadRequestOption<any>) { | ||||
|   const { api } = props; | ||||
|   let { api } = props; | ||||
|   if (!api || !isFunction(api)) { | ||||
|     console.warn('upload api must exist and be a function'); | ||||
|     return; | ||||
|     api = useUpload(props.directory).httpRequest; | ||||
|   } | ||||
|   try { | ||||
|     // 上传文件 | ||||
|  |  | |||
|  | @ -7,8 +7,7 @@ import { computed, unref } from 'vue'; | |||
| import { useAppConfig } from '@vben/hooks'; | ||||
| import { $t } from '@vben/locales'; | ||||
| 
 | ||||
| import CryptoJS from 'crypto-js'; | ||||
| 
 | ||||
| // import CryptoJS from 'crypto-js';
 | ||||
| import { createFile, getFilePresignedUrl, uploadFile } from '#/api/infra/file'; | ||||
| import { baseRequestClient } from '#/api/request'; | ||||
| 
 | ||||
|  | @ -81,7 +80,7 @@ export function useUploadType({ | |||
| } | ||||
| 
 | ||||
| // TODO @芋艿:目前保持和 admin-vue3 一致,后续可能重构
 | ||||
| export const useUpload = () => { | ||||
| export const useUpload = (directory?: string) => { | ||||
|   // 后端上传地址
 | ||||
|   const uploadUrl = getUploadUrl(); | ||||
|   // 是否使用前端直连上传
 | ||||
|  | @ -97,7 +96,7 @@ export const useUpload = () => { | |||
|       // 1.1 生成文件名称
 | ||||
|       const fileName = await generateFileName(file); | ||||
|       // 1.2 获取文件预签名地址
 | ||||
|       const presignedInfo = await getFilePresignedUrl(fileName); | ||||
|       const presignedInfo = await getFilePresignedUrl(fileName, directory); | ||||
|       // 1.3 上传文件
 | ||||
|       return baseRequestClient | ||||
|         .put(presignedInfo.uploadUrl, file, { | ||||
|  | @ -107,13 +106,13 @@ export const useUpload = () => { | |||
|         }) | ||||
|         .then(() => { | ||||
|           // 1.4. 记录文件信息到后端(异步)
 | ||||
|           createFile0(presignedInfo, fileName, file); | ||||
|           createFile0(presignedInfo, file); | ||||
|           // 通知成功,数据格式保持与后端上传的返回结果一致
 | ||||
|           return { data: presignedInfo.url }; | ||||
|         }); | ||||
|     } else { | ||||
|       // 模式二:后端上传
 | ||||
|       return uploadFile({ file }, onUploadProgress); | ||||
|       return uploadFile({ file, directory }, onUploadProgress); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|  | @ -134,18 +133,13 @@ export const getUploadUrl = (): string => { | |||
|  * 创建文件信息 | ||||
|  * | ||||
|  * @param vo 文件预签名信息 | ||||
|  * @param name 文件名称 | ||||
|  * @param file 文件 | ||||
|  */ | ||||
| function createFile0( | ||||
|   vo: InfraFileApi.FilePresignedUrlRespVO, | ||||
|   name: string, | ||||
|   file: File, | ||||
| ) { | ||||
| function createFile0(vo: InfraFileApi.FilePresignedUrlRespVO, file: File) { | ||||
|   const fileVO = { | ||||
|     configId: vo.configId, | ||||
|     url: vo.url, | ||||
|     path: name, | ||||
|     path: vo.path, | ||||
|     name: file.name, | ||||
|     type: file.type, | ||||
|     size: file.size, | ||||
|  | @ -160,12 +154,13 @@ function createFile0( | |||
|  * @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.slice(Math.max(0, file.name.lastIndexOf('.'))); | ||||
|   return `${sha256}${ext}`; | ||||
|   // // 读取文件内容
 | ||||
|   // const data = await file.arrayBuffer();
 | ||||
|   // const wordArray = CryptoJS.lib.WordArray.create(data);
 | ||||
|   // // 计算SHA256
 | ||||
|   // const sha256 = CryptoJS.SHA256(wordArray).toString();
 | ||||
|   // // 拼接后缀
 | ||||
|   // const ext = file.name.slice(Math.max(0, file.name.lastIndexOf('.')));
 | ||||
|   // return `${sha256}${ext}`;
 | ||||
|   return file.name; | ||||
| } | ||||
|  |  | |||
|  | @ -48,11 +48,10 @@ export function useFormSchema(): VbenFormSchema[] { | |||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     // TODO @芋艿:图片上传
 | ||||
|     { | ||||
|       fieldName: 'logo', | ||||
|       label: '应用图标', | ||||
|       component: 'UploadImage', | ||||
|       component: 'ImageUpload', | ||||
|       componentProps: { | ||||
|         limit: 1, | ||||
|       }, | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 YunaiV
						YunaiV