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
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.
This is my workaround
with zipfile.ZipFile(io.BytesIO(file.read()), 'r') as zip:
This is fixed in python 3.11. Changelog for reference
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
This is a known Python bug:
SpooledTemporaryFile does not fully satisfy the abstract for IOBase.
Namely,seekable
,readable
, andwritable
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.
This is my workaround
with zipfile.ZipFile(io.BytesIO(file.read()), 'r') as zip:
This is fixed in python 3.11. Changelog for reference