FastAPI - 使用 Axios 上传文件 - 错误请求

FastAPI - Upload files with Axios - Bad Request

客户代码:

!<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title></title>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>

<form id="uploadForm" role="form" method="post" enctype=multipart/form-data>
    <input type="file" id="file" name="file" multiple>
    <input type=button value=Upload onclick="uploadFile()">
</form>

<script type="text/javascript">
function uploadFile() {
   var formData = new FormData();
    var imagefile = document.querySelector('#file');
    formData.append("images", imagefile.files);
    axios.post('http://127.0.0.1:8000/upload', formData, {
        headers: {
          'Content-Type': 'multipart/form-data'
        }
    })
}
</script>
</body>
</html>

服务器代码:

from fastapi import FastAPI, File, UploadFile, FastAPI
from typing import Optional, List
from fastapi.responses import FileResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware

...

def save_file(filename, data):
    with open(filename, 'wb') as f:
        f.write(data)
        print('file saved')

@app.post("/upload")
async def upload(files: List[UploadFile] = File(...)):
    print(files)
    for file in files:
        contents = await file.read()
        save_file(file.filename, contents)
        print('file received')

    return {"Uploaded Filenames": [file.filename for file in files]}

我收到以下错误:

 ←[32mINFO←[0m:     127.0.0.1:10406 - "←[1mPOST /upload HTTP/1.1←[0m" ←[31m400 Bad Request←[0m 

我尝试通过表单操作上传单个文件并且一切正常,但我需要上传两个文件。

首先,当上传文件或表单数据时,应该使用他们endpoint/route中定义的相同表单键。在您的情况下,即 files。因此,在客户端,您应该使用该密钥而不是 images.

其次,上传多个文件的方法是循环遍历文件数组(即代码中的imagefile.files)并将每个文件添加到FormData对象。

第三,错误似乎是由于Axios 0.27.1版本中的某些bug/changes引起的。这是最近 related issue on GitHub. Using the latest version, i.e., 0.27.2, from cdnjs here 解决问题的方法。

或者,您可以使用 Fetch API, similar to this answer (you can add the list of files in the same way as shown in the Axios example below). In Fetch API, when uploading files to the server, you should not explicitly set the Content-Type header on the request, as explained here

使用 Axios 的工作示例:

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <title>Upload Files</title>
      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js"></script>   
   </head>
   <body>
      <input type="file" id="fileInput" multiple><br>
      <input type="button" value="Upload" onclick="uploadFile()">
      <script type="text/javascript">
         function uploadFile() {
             var fileInput = document.querySelector('#fileInput'); 
         
             if (fileInput.files[0]) {
                var formData = new FormData();
                for (const file of fileInput.files)
                    formData.append('files', file);

                 axios({
                         method: 'post',
                         url: '/upload',
                         data: formData,
                         headers: {
                             'Accept': 'application/json',
                             'Content-Type': 'multipart/form-data'
                         }
                     })
                     .then(function(response) {
                         console.log(response);
                     })
                     .catch(function(response) {
                         console.log(response);
                     });
             }
         }
      </script>
   </body>
</html>

从 Axios v 0.27.2 开始,您可以 do this easily:

axios
    .postForm("https://httpbin.org/post", document.querySelector("#fileInput").files)

所有文件都将使用 files[] 密钥提交。

更详细的示例:

    axios.postForm("https://httpbin.org/post", {
      "myField": "foo"
      "myJson{}": {x:1, y: 'bar'}, 
      "files[]": document.querySelector("#fileInput").files
    })