Fastapi uploaded file not saved

I am a beginner in fastapi. I am trying to save a zip file uploaded from the front end

from fastapi import FastAPI, File, UploadFile
import os
import zipfile
import shutil
app = FastAPI() 


@app.post('/FileUpload/')
def FileUpload(file: UploadFile = File(...)):
    dest=os.path.dirname(os.path.dirname( os.path.abspath(__file__))) +f'FILES{file.filename}'   
    if zipfile.is_zipfile(file.file):   
        try:            
            with open(dest, "wb+") as file_object:
                shutil.copyfileobj(file.file, file_object)
        finally:
            file.file.close()
    else:
        print("Invalid File type")
    return {"Status": True}

Currently, only a 1KB file of the same file name is saved in the folder, not the uploading file. Am I missing something here or any issues in this approach?

Appreciate your help and Thank you very much in advance.

Answer

Even though it’s not a problem in this case, I would hihghly suggest using pathlib.Path() to define the destination for your upload

The issue seems to be related to zipfile.is_zipfile(). Even though the documentation doesn’t specify this, the function apparently “scans” the whole file and leaves the stream at the end (not sure whether this is a bug or a feature). At the same time, the shutil.copyfileobj() docs tell us:

[…] Note that if the current file position of the fsrc object is not 0, only the contents from the current file position to the end of the file will be copied.

To better understand what’s going on, you can add a .tell() at various positions in the code.

To fix the issue, you can use .seek(0) to reset the stream position:

from pathlib import Path
from fastapi import FastAPI, File, UploadFile
import zipfile
import shutil

app = FastAPI()

@app.post('/FileUpload/')
def FileUpload(file: UploadFile = File(...)):
    dest = Path(__file__).resolve().parents[1] / "FILES" / file.filename
#          ^^^^
#          Not directly related to your problem but more elegant imho.

    print(file.file.tell())
    # 0  <- stream position is at the beginning

    if zipfile.is_zipfile(file.file):   
        print(file.file.tell())
        # <file size in bytes>  <- stream position is at the end

        # Reset the file stream:
        file.file.seek(0)

        print(file.file.tell())
        # 0  <- stream position is back at the beginning

        try:
            with open(dest, "wb+") as file_object:
                shutil.copyfileobj(file.file, file_object)
        finally:
            file.file.close()
    else:
        print("Invalid File type")

    return {"Status": True}