Skip to content

文件上传联调

说明

文件上传涉及前端 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

VAxios 类内置 uploadFile 方法:

typescript
// 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,
    });
}

Upload 组件使用

vue
<template>
    <Upload v-model="formData.cover" :limit="1" accept="image/*" />
</template>

<script setup>
const formData = ref({
    name: '',
    cover: '',  // 存储文件 URL
})
</script>

Upload 组件内部调用上传接口:

typescript
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(文件字段)

返回:

json
{
    "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
}

URL 拼接

后端拼接

后端 save_file() 函数处理文件迁移后返回相对路径,get_file_url() 拼接完整 URL:

python
def get_file_url(path):
    if not path:
        return ""
    if path.find(FASTAPI_FILE_URL) != -1:
        return path
    return FASTAPI_FILE_URL + path

前端显示

前端显示图片时,需要拼接完整的 URL:

typescript
const getFileUrl = (path) => {
    if (!path) return '';
    if (path.startsWith('http')) return path;
    return import.meta.env.VITE_GLOB_FILE_URL + path;
};

临时文件迁移

业务数据提交时,后端 service 调用 save_file() 将临时文件迁移到正式目录:

python
# 临时路径: {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 locationNginx 配置文件代理路径

温馨提示

开发环境和生产环境的文件访问域名不同,需要分别配置。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 显示文件。

小蚂蚁云团队 · 提供技术支持