Become a sponsor

说明
文件上传涉及前端 Upload 组件、后端上传接口和 URL 拼接三个环节,需要前后端协同配置。
1. 前端 Upload 组件选择文件
2. VAxios.uploadFile() 发送 multipart/form-data POST 请求到 /api/v1/upload/uploadFile
3. 后端校验文件(扩展名、大小、魔术字节)
4. 后端保存文件到临时目录,返回文件信息(fileUrl 等)
5. 前端将 fileUrl 存入业务字段
6. 业务数据提交时携带 fileUrl
7. 后端 save_file() 将临时文件迁移到正式目录VAxios 类内置 uploadFile 方法:
// src/utils/http/axios/Axios.ts
uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
const formData = new window.FormData();
const customFilename = params.name || 'file';
if (params.filename) {
formData.append(customFilename, params.file, params.filename);
} else {
formData.append(customFilename, params.file);
}
if (params.data) {
Object.keys(params.data).forEach((key) => {
const value = params.data![key];
if (Array.isArray(value)) {
value.forEach((item) => formData.append(`${key}[]`, item));
return;
}
formData.append(key, params.data![key]);
});
}
return this.axiosInstance.request<T>({
method: 'POST',
data: formData,
headers: {
'Content-type': ContentTypeEnum.FORM_DATA,
ignoreCancelToken: true,
},
...config,
});
}<template>
<Upload v-model="formData.cover" :limit="1" accept="image/*" />
</template>
<script setup>
const formData = ref({
name: '',
cover: '', // 存储文件 URL
})
</script>Upload 组件内部调用上传接口:
const handleUpload = async (file) => {
const formData = new FormData();
formData.append('file', file);
const res = await uploadFile(formData);
// res.data.fileUrl = "http://127.0.0.1:8031/api/file/20250306/xxx.jpg"
emit('update:modelValue', res.data.fileUrl);
};POST /api/v1/upload/uploadFile
Content-Type: multipart/form-data
参数: file(文件字段)返回:
{
"code": 0,
"data": {
"originalName": "photo.jpg",
"fileExtension": "jpg",
"fileType": "image/jpeg",
"fileSize": 306995,
"fileName": "2025030614302512345.jpg",
"filePath": "/20250306/2025030614302512345.jpg",
"fileUrl": "http://127.0.0.1:8031/api/file/20250306/2025030614302512345.jpg"
},
"msg": "上传成功",
"ok": true
}后端 save_file() 函数处理文件迁移后返回相对路径,get_file_url() 拼接完整 URL:
def get_file_url(path):
if not path:
return ""
if path.find(FASTAPI_FILE_URL) != -1:
return path
return FASTAPI_FILE_URL + path前端显示图片时,需要拼接完整的 URL:
const getFileUrl = (path) => {
if (!path) return '';
if (path.startsWith('http')) return path;
return import.meta.env.VITE_GLOB_FILE_URL + path;
};业务数据提交时,后端 service 调用 save_file() 将临时文件迁移到正式目录:
# 临时路径: {temp}/{日期}/{文件名}
# 正式路径: {upload_dir}/{分类}/{日期}/{文件名}
def save_file(url, directory):
disk_path = _url_to_disk_path(url)
if disk_path.startswith(FASTAPI_TEMP_PATH):
target_dir = os.path.join(FASTAPI_UPLOAD_DIR, directory, time.strftime('%Y%m%d'))
new_path = os.path.join(target_dir, os.path.basename(disk_path))
shutil.move(disk_path, new_path)
return _disk_to_url_path(new_path)
return _disk_to_url_path(disk_path)确保以下配置一致:
| 配置项 | 位置 | 说明 |
|---|---|---|
FASTAPI_FILE_URL | 后端 .env | 文件访问域名 |
VITE_GLOB_FILE_URL | 前端 .env | 文件访问域名 |
Nginx location | Nginx 配置 | 文件代理路径 |
温馨提示
开发环境和生产环境的文件访问域名不同,需要分别配置。VITE_GLOB_FILE_URL 通过 useGlobSetting() 获取,生产环境通常使用独立的文件服务器或 CDN。
1. 检查 VITE_GLOB_FILE_URL 配置是否正确
2. 检查 Nginx 是否配置了 /api/file/ 代理
3. 检查文件是否已从临时目录迁移到正式目录1. 检查文件扩展名是否在白名单中
2. 检查文件大小是否超过 UPLOAD_MAX_SIZE_MB 限制
3. 检查后端服务是否正常运行文件上传通过 VAxios.uploadFile() 发送 multipart/form-data 请求,后端保存到临时目录并返回 fileUrl。业务提交时后端 save_file() 迁移到正式目录。前端通过 VITE_GLOB_FILE_URL 拼接完整 URL 显示文件。