Read a zip file sent to a flask server without storing it on disk
flask, python, zip
Solution
First, get the code to get the zip file
from flask import Flask, request
app = Flask(__name__)
@app.route("/",methods=["GET"])
def page_name_get():
return """<form action="." method="post" enctype=multipart/form-data>
<input type="file" accept="application/zip" name="data_zip_file" accept="application/zip" required>
<button type="submit">Send zip file!</button>
</form>"""
app.run()
This is how the post request function should look like
import zipfile
@app.route("/",methods=["POST"])
def page_name_post():
file = request.files['data_zip_file']
file_like_object = file.stream._file
zipfile_ob = zipfile.ZipFile(file_like_object)
file_names = zipfile_ob.namelist()
# Filter names to only include the filetype that you want:
file_names = [file_name for file_name in file_names if file_name.endswith(".txt")]
files = [(zipfile_ob.open(name).read(),name) for name in file_names]
return str(files)
Now I will go over this line by line
`file = request.files['data_zip_file']` First, you need to get the file object from the request this is is an instance of the `werkzeug.datastructures.FileStorage` class.
`file_like_object = file.stream._file` here you first take the stream attribute of the `werkzeug.datastructures.FileStorage` this is the input stream of the file. This will return an instance of `tempfile.SpooledTemporaryFile` a class used for temporary files. From that instance, you take the ._file attribute. This will return an instance of `tempfile._TemporaryFileWrapper` This is enough like an `io.BytesIO` to be understood by the zipfile.ZipFile class.
`zipfile_ob = zipfile.ZipFile(file_like_object)` here you create the `zipfile.Zipfile` object
Now you should be able to do pretty much everything you would want to do with the zip. To select a file from the zip use the `zipfile_ob.open()` method and pass in the path to the file you want to open.
To get those paths we use `file_names = zipfile_ob.namelist()` this will return a list with strings of all the paths to all the files and directories in the zip.
You can then filter those names with `file_names = [file_name for file_name in file_names if file_name.endswith(".txt")]`
All those paths you want are now in `file_names`. Then you can extract the data of those files using the open function.
`files = [(zipfile_ob.open(name).read(),name) for name in file_names]`
In the example given I keep the paths to the file in the final list but if you don't want that you can use:
`files = [zipfile_ob.open(name).read() for name in file_names]` or use some other way to go over the file. If you want more info about the files there is also the `infolist()` method that can be used instead of the `namelist()` this will return a list of ZipInfo Objects instead of a list of just strings. These objects hold some more data about all the files.
Problem
I would like to read all the files in a zip file of a specific type sent to a flask server via a form post request without having to store the zip file on disk.