Skip to main content

Upload File: multipart/form-data Usage

· 11 min read
Kimi Gao
Fullstack & AI

multipart/form-data v.s. application/x-www-form-urlencoded

multipart/form-dataapplication/x-www-form-urlencoded 是两种常用的 HTTP 表单数据编码方式,它们在 Web 应用程序中用于提交表单数据到服务器。尽管它们的目的相似,但它们在实现和使用上有一些关键的差异。

相同点

  1. 表单数据传输:它们都用于通过 HTTP POST 请求传输表单数据到服务器。
  2. 浏览器支持:现代 Web 浏览器都支持这两种编码类型,并能够自动处理表单数据的编码和解码。
  3. 数据类型:它们可以传输文本字段,如文本输入、选择框、复选框等。

差异点

  1. 二进制数据支持

    • multipart/form-data:支持文件和二进制数据的传输。它使用特殊的分隔符来分隔各个表单字段,并且每个字段都有自己的内容类型(MIME type)。
    • application/x-www-form-urlencoded:不支持文件和二进制数据的传输。所有数据都被编码为键值对,并且对于非 ASCII 字符使用百分比编码(URL 编码)。
  2. 数据结构

    • multipart/form-data:数据以多部分(multipart)的形式发送,每个部分可以包含不同类型的数据,如文本和文件。
    • application/x-www-form-urlencoded:数据以单一的 URL 编码字符串的形式发送,所有的数据都编码在 URL 的查询参数中。
  3. 请求体大小

    • multipart/form-data:由于支持文件和二进制数据,请求体的大小通常较大。
    • application/x-www-form-urlencoded:请求体通常较小,因为它只包含文本数据。
  4. 请求头

    • multipart/form-data:请求头中包含 Content-Type,其值后面跟着一个边界字符串(boundary),用于分隔消息体中的各个部分。
    • application/x-www-form-urlencoded:请求头中包含 Content-Type,其值为 application/x-www-form-urlencoded
  5. 解析复杂性

    • multipart/form-data:服务器端需要解析多部分消息体,这可能涉及到更复杂的逻辑来处理不同类型的数据。
    • application/x-www-form-urlencoded:服务器端通常更容易解析,因为数据以简单的键值对形式存在。
  6. 用途

    • multipart/form-data:通常用于文件上传和提交包含文件、图片等二进制数据的表单。
    • application/x-www-form-urlencoded:通常用于提交文本数据的表单,如登录表单、搜索表单等。

总结来说,multipart/form-data 编码类型适合于文件上传和传输二进制数据的场景,而 application/x-www-form-urlencoded 则适用于传输文本数据的表单。开发者应根据实际需求选择合适的编码类型来处理表单数据。

React + FastAPI

multipart/form-data 是一种编码类型,它在 Web 开发中有着广泛的应用场景,尤其是在需要上传文件的情况下。结合 React 和 FastAPI,我们可以创建一个端到端的文件上传解决方案。以下是 multipart/form-data 在这种场景下的使用说明:

React 端

  1. 创建表单:在 React 应用中,创建一个表单,允许用户选择文件进行上传。你需要设置表单的 enctype 属性为 multipart/form-data,以便正确编码表单数据。

    <form onSubmit={handleUpload}>
    <input type="file" name="file" accept="image/*" />
    <button type="submit">Upload</button>
    </form>
  2. 处理表单提交:使用 React Hook Form 或其他表单库来处理表单提交事件。在提交事件中,你可以使用 Axios 或其他 HTTP 客户端库来发送请求。

    const handleUpload = async event => {
    event.preventDefault();
    const formData = new FormData(event.target);
    // 发送请求...
    };
  3. 发送请求:使用 Axios 的 post 方法,并传递 FormData 对象作为请求体,以便发送文件数据。

    const handleUpload = async event => {
    event.preventDefault();
    const formData = new FormData(event.target);
    try {
    const response = await axios.post('/upload', formData, {
    headers: {
    'Content-Type': 'multipart/form-data',
    },
    });
    console.log(response.data);
    } catch (error) {
    console.error(error);
    }
    };

FastAPI 端

  1. 定义路由:在 FastAPI 应用中,定义一个路由来处理文件上传请求。使用 FileUploadFile 类型来声明文件参数。

    from fastapi import FastAPI, File, UploadFile

    app = FastAPI()

    @app.post("/upload")
    async def upload_file(file: UploadFile = File(...)):
    # 处理文件...
    return {"filename": file.filename}
  2. 安装依赖:为了处理 multipart/form-data,FastAPI 需要 python-multipart 库。安装它以支持流式解析。

    pip install python-multipart
  3. 处理文件:在 FastAPI 路由函数中,你可以读取上传的文件,将其保存到服务器或进行进一步处理。

    @app.post("/upload")
    async def upload_file(file: UploadFile = File(...)):
    file_path = f"./uploads/{file.filename}"
    with open(file_path, "wb") as buffer:
    buffer.write(await file.read())
    return {"filename": file.filename}
  4. 多文件上传:FastAPI 同样支持多文件上传。只需将文件参数声明为 List[UploadFile] 类型即可。

    @app.post("/uploadfiles")
    async def upload_files(files: List[UploadFile] = File(...)):
    # 处理多个文件...
    return {"filenames": [file.filename for file in files]}

通过结合 React 和 FastAPI,你可以创建一个功能完善的文件上传系统,它能够在前端通过用户友好的界面选择和上传文件,并在后端安全、高效地处理这些文件。使用 multipart/form-data 编码确保了文件数据的正确传输,同时也支持其他表单数据的发送。

antd or arco + FastAPI

在 React 应用中,antdUpload 组件提供了一个简单且强大的界面来处理文件上传。结合 multipart/form-data 和 FastAPI 后端,你可以创建一个高效的文件上传功能。以下是如何使用 antd Upload 组件与 FastAPI 后端结合的步骤:

React 端使用 antd Upload 组件

  1. 创建上传组件:在你的 React 组件中,使用 antdUpload 组件来创建一个上传界面。Upload 组件提供了多种配置选项,包括自定义请求头、上传方法和上传地址等。

    import { Upload, Button, message } from 'antd';
    import { UploadOutlined } from '@ant-design/icons';

    const props = {
    action: '/upload', // 你的 FastAPI 后端上传接口地址
    onChange(info) {
    if (info.file.status !== 'uploading') {
    console.log(info.file, info.fileList);
    }
    if (info.file.status === 'done') {
    message.success(`${info.file.name} file uploaded successfully`);
    } else if (info.file.status === 'error') {
    message.error(`${info.file.name} file upload failed.`);
    }
    },
    // 可以添加其他属性,如 headers, data 等
    };

    const MyUploadComponent = () => (
    <Upload {...props}>
    <Button icon={<UploadOutlined />}>Click to Upload</Button>
    </Upload>
    );

    export default MyUploadComponent;
  2. 自定义上传方法(可选):如果你需要自定义上传方法,可以通过设置 customRequest 属性来实现。例如,你可以使用 Axios 来发送自定义的 HTTP 请求。

    const customRequest = async ({ file, onSuccess, onError }) => {
    const formData = new FormData();
    formData.append('file', file);
    try {
    const response = await axios.post('/upload', formData, {
    headers: {
    'Content-Type': 'multipart/form-data',
    },
    });
    onSuccess(response.data);
    } catch (error) {
    onError(error);
    }
    };

    const props = {
    // ...其他属性
    customRequest,
    };

React 端使用 arco-upload 组件

  1. 创建上传组件:在你的 React 组件中,使用 arco-upload 组件来创建一个上传界面。arco-upload 组件提供了多种配置选项,包括自定义请求头、上传方法和上传地址等。

    import React from 'react';
    import { Upload, Button } from 'arco-design';

    const MyUploadComponent = () => {
    const props = {
    action: '/upload', // 你的 FastAPI 后端上传接口地址
    onChange: info => {
    if (info.file.status !== 'uploading') {
    console.log(info.file, info.fileList);
    }
    if (info.file.status === 'done') {
    // 可以在这里显示上传成功的提示信息
    console.log(`File uploaded successfully: ${info.file.name}`);
    } else if (info.file.status === 'error') {
    // 可以在这里显示上传失败的提示信息
    console.error(`File upload failed: ${info.file.name}`);
    }
    },
    // 可以添加其他属性,如 headers, data 等
    };

    return (
    <Upload {...props}>
    <Button type="primary">Click to Upload</Button>
    </Upload>
    );
    };

    export default MyUploadComponent;
  2. 自定义上传方法(可选):如果你需要自定义上传方法,可以通过设置 customRequest 属性来实现。例如,你可以使用 Axios 来发送自定义的 HTTP 请求。

    const customRequest = options => {
    return new Promise((resolve, reject) => {
    const { file, onProgress, onSuccess, onError } = options;
    const formData = new FormData();
    formData.append('file', file);

    Axios.post('/upload', formData, {
    headers: {
    'Content-Type': 'multipart/form-data',
    },
    onUploadProgress: event => {
    onProgress({ percent: (event.loaded / event.total) * 100 });
    },
    })
    .then(response => {
    onSuccess(response.data);
    })
    .catch(error => {
    onError(error);
    });
    });
    };

    const props = {
    // ...其他属性
    customRequest,
    };

FastAPI 端处理上传

  1. 创建 FastAPI 应用:创建一个新的 FastAPI 应用,并定义一个上传接口。使用 FileUploadFile 类型来接收上传的文件。

    from fastapi import FastAPI, File, UploadFile

    app = FastAPI()

    @app.post("/upload")
    async def upload_file(file: UploadFile = File(...)):
    # 保存文件到服务器或其他处理
    filename = file.filename
    with open(f"./{filename}", "wb") as buffer:
    buffer.write(await file.read())
    return {"message": f"File {filename} uploaded successfully."}
  2. 运行 FastAPI 应用:使用 uvicorn 或其他 ASGI 服务器来运行你的 FastAPI 应用。

    uvicorn your_fastapi_app:app --reload

通过上述步骤,你可以在 React 应用中使用 antd Upload 组件来提供一个用户友好的文件上传界面,同时在后端使用 FastAPI 来处理上传的文件。这种结合方式不仅提高了用户体验,也使得文件上传过程更加稳定和可靠。