Can't open and read content of an uploaded zip file with FastAPI

Question:

I am currently developing a little backend project for myself with the Python Framework FastAPI. I made an endpoint, where the user should be able to upload 2 files, while the first one is a zip-file (which contains X .xmls) and the latter a normal .xml file.

The code is as follows:

@router.post("/sendxmlinzip/")
def create_upload_files_with_zip(files: List[UploadFile] = File(...)):
    if not len(files) == 2:
        raise Httpex.EXPECTEDTWOFILES
    my_file = files[0].file
    zfile = zipfile.ZipFile(my_file, 'r')
    filelist = []
    for finfo in zfile.infolist():
        print(finfo)
        ifile = zfile.open(finfo)
        line_list = ifile.readlines()
        print(line_list)

This should print the content of the files, that are in the .zip file, but it raises the Exception

AttributeError: ‘SpooledTemporaryFile’ object has no attribute ‘seekable’

In the row ifile = zfile.open(finfo)

Upon approximately 3 days research with a lot of trial and error involved, trying to use different functions such as .read() or .extract(), I gave up. Because the python docs literally state, that this should be possible in this way…

For you, who do not know about FastAPI, it’s a backend fw for Restful Webservices and is using the starlette datastructure for UploadFile. Please forgive me, if I have overseen something VERY obvious, but I literally tried to check every corner, that may have been the possible cause of the error such as:

  • Check, whether another implementation is possible
  • Check, that the .zip file is correct
  • Check, that I attach the correct file (lol)
  • Debug to see, whether the actual data, that comes to the backend is indeed the .zip file
Asked By: Ayibogan

||

Answers:

This is a known Python bug:

SpooledTemporaryFile does not fully satisfy the abstract for IOBase.
Namely, seekable, readable, and writable are missing.

This was discovered when seeking a SpooledTemporaryFile-backed lzma
file.

As @larsks suggested in his comment, I would try writing the contents of the spooled file to a new TemporaryFile, and then operate on that. As long as your files aren’t too large, that should work just as well.

Answered By: ron rothman

This is my workaround

with zipfile.ZipFile(io.BytesIO(file.read()), 'r') as zip:
Answered By: Vinh Ta

This is fixed in python 3.11. Changelog for reference

Answered By: Binoy