简体   繁体   中英

How do you save multitype/form data to a hard file in Python with FastAPI UploadFile?

https://fastapi.tiangolo.com/tutorial/request-files/

*Solved Below *

I've gotten an appropriately sized array of bytes and I'm not sure how to parse it correctly to save received form file data.

Almost working:

@app.post("/uploadfiles/")
async def create_files(files: List[bytes] = File(...)):
    out_file = open("files/1.txt", "w") # open for [w]riting as [b]inary
    out_file.write( str([(file) for file in files]))
    out_file.close()
    return {"file_sizes": [len(file) for file in files]}

The script results in a file that is no longer a readable .png. I assume I'm using the libraries incorrectly but I'm not sure which to start with: HTMLResponse, FastAPI, multipart or List maybe? Any ideas or docs on how to properly parse these bytes back together again?

Edit

Per a break and once over review of the FastAPI docs, I spotted that this particular array of byte data is multipart/form-data data (so maybe I'll find a python library for reading form data/images):

Here is a small example (first ~100chars) of the data that is generated:

[b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x03\xe8\x00\x00\x01\xf4\x08\x06\x00\x00\x00\xae(\x07-\x00\x00\x01\x86iCCPICC profile\x00\x00(\x91}\x91=H\

I did it!!!

The secret was pythons native byte recognition libraries and order of ops!!

@app.post("/uploadfiles/")
async def create_files(files: bytes = File(...)):
    out_file = open("files/1.jpg", "wb") # open for [w]riting as [b]inary
    out_file.write( bytes([(file) for file in files]))
    out_file.close()

Still work to do on the file naming system :) GL Everyone!

Edit 2

Since the below answer didn't function, I scripted a file name appender:

@app.post("/uploadfiles/")

async def create_files(files: bytes = File(...)):
    with open('C:/data/sample.txt', 'r', encoding='utf-8') as g:
        data=g.readlines()

    for line in data:
        counter = int(line)
        with open('C:/data/sample.txt', 'w') as f:
            counter = counter + 1
            f.write(str(counter))

    out_file = open("files/" + str(counter) + ".jpg", "wb") # open for [w]riting as [b]inary
    out_file.write( bytes([(file) for file in files]))
    out_file.close()

Thanks for the help everyone! Happy hacking :^)

Edit 3

I've been informed that the method of which I am posting could be incorrect practice in conjunction with FastAPI so to better understand this I am posting the relevant javascript that posts to the backend:

Here is the relevant code from my reactjs form post script:

 onSubmit = (e) => {
    e.preventDefault();

    const formData = new FormData();
if (this.state.images != null) {

    var form = document.getElementById("apiupform");
    document.getElementById("apiupform").hidden = true;
    Array.from(this.state.images).forEach((image) => {
      formData.append("files", image);
    });

    axios
      .post(`https://****.com/uploadfiles/`, formData, {
        headers: {
          "Content-Type": "multipart/form-data",
        },
      })

Thank you everyone for the help with this work. I will update it as I find improvements :)

You can save the uploaded files this way,

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/upload-file/")
async def create_upload_file(uploaded_file: UploadFile = File(...)):
    file_location = f"files/{uploaded_file.filename}"
    with open(file_location, "wb+") as file_object:
        file_object.write(uploaded_file.file.read())
    return {"info": f"file '{uploaded_file.filename}' saved at '{file_location}'"}

OR,
maybe a more efficient way using the shutil.copyfileobj(...) method as,


from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/upload-file/")
async def create_upload_file(uploaded_file: UploadFile = File(...)):
    file_location = f"files/{uploaded_file.filename}"
    with open(file_location, "wb+") as file_object:
        
    return {"info": f"file '{uploaded_file.filename}' saved at '{file_location}'"}

Sample Swagger

Swagger 截图

I would use an asynchronous library like aiofiles for file operations.

Since you are running your app inside an event loop the file writing operation will block the entire execution of your app.

import aiofiles


@app.post("/uploadfiles/")
async def create_files(files: bytes = File(...)):
    async with aiofiles.open("files/1.jpg", "wb") as f:
        await f.write(bytes([(file) for file in files]))

this is what worked for me..

@app.post("/fileUpload/")
def fileUpload(ufile: UploadFile = File(...)):
    s = str(ufile.file.read(), 'utf-8')

    with open(ufile.filename, "w+") as buffer:
        buffer.write(s)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM