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.

Original source