后端: - 新增 StorageService 集成 MinIO 对象存储 - 新增 FileService 和 FileController 处理文件上传 - 支持头像上传场景,含文件类型和大小验证 - 支持生成临时访问 URL 前端: - 新增 AvatarUpload 组件,支持拖拽和点击上传 - 新增 useUploadAvatar 和 useFileUrl hooks - 新增 file.service.ts 封装文件 API Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
71 lines
1.5 KiB
TypeScript
71 lines
1.5 KiB
TypeScript
import { API_ENDPOINTS } from '@/config/constants';
|
|
import { httpClient, type CustomRequestConfig } from '@/lib/http';
|
|
|
|
/** 文件用途 */
|
|
export type FilePurpose = 'avatar' | 'attachment';
|
|
|
|
/** 文件响应 */
|
|
export interface FileResponse {
|
|
id: string;
|
|
filename: string;
|
|
mimeType: string;
|
|
size: number;
|
|
purpose: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
/** 临时访问 URL 响应 */
|
|
export interface PresignedUrlResponse {
|
|
url: string;
|
|
expiresIn: number;
|
|
}
|
|
|
|
/**
|
|
* 上传文件
|
|
*/
|
|
export async function uploadFile(
|
|
file: File,
|
|
purpose: FilePurpose,
|
|
): Promise<FileResponse> {
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
formData.append('purpose', purpose);
|
|
|
|
const config: CustomRequestConfig = {
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data',
|
|
},
|
|
skipEncryption: true,
|
|
};
|
|
|
|
const response = await httpClient.post<FileResponse>(
|
|
`${API_ENDPOINTS.FILES}/upload`,
|
|
formData,
|
|
config,
|
|
);
|
|
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* 获取文件临时访问 URL
|
|
*/
|
|
export async function getFileUrl(
|
|
fileId: string,
|
|
expiresIn?: number,
|
|
): Promise<PresignedUrlResponse> {
|
|
const params = expiresIn ? { expiresIn } : {};
|
|
const response = await httpClient.get<PresignedUrlResponse>(
|
|
`${API_ENDPOINTS.FILES}/${fileId}/url`,
|
|
{ params },
|
|
);
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* 删除文件
|
|
*/
|
|
export async function deleteFile(fileId: string): Promise<void> {
|
|
await httpClient.delete(`${API_ENDPOINTS.FILES}/${fileId}`);
|
|
}
|