Search code examples
python-3.xflaskhttprequestgzipfile-storage

How to decompress a FileStorage object over gzip in Flask


I am sending a compressed file (compressed with gzip) to my flask application over an HTTP multipart/form-data POST request. The request works correctly and when I print out the value of the key to the console it prints this: <FileStorage: 'compressed.zip' ('application/zip')>. This was compressed using Windows zip compressed but will be compressed using the application/gzip type. The key for the compressed.zip file is called compressed. My question is how can I decompress the gzip file and then store the FileStorage object to a specific path on the backend? Here is the code that I have currently:

from flask import request, Blueprint, make_response
import gzip
blueprint = Blueprint("blueprint", __name__)

@blueprint.route("/compressed-post", methods=["POST"])
def compressed_post():
    if request.method == "POST":
        file = request.files["compressed"]
        print(file)
        return make_response("", 200)
    else:
        return make_response("", 400)

Solution

  • Given your code snippet...

    @blueprint.route("/compressed-post", methods=["POST"])
    def compressed_post():
        if request.method == "POST":
            file = request.files["compressed"]
    

    ... you just have to pass in file._file into a ZipFile constructor, as ZipFile expects a file-like-object (or a complete path, which we do not have here, as the file was not yet saved to disk).

    Then, you can use ZipFiles extractAll method to extract the files to a path you like.

    Also see

    https://docs.python.org/3/library/zipfile.html#zipfile.ZipFile.extractall

    So, the complete code should something like this:

    from flask import request, Blueprint, make_response
    import gzip
    from zipfile import ZipFile
    blueprint = Blueprint("blueprint", __name__)
    
    @blueprint.route("/compressed-post", methods=["POST"])
    def compressed_post():
        if request.method == "POST":
            file = request.files["compressed"]
            zip_handle = ZipFile(file._file)
            zip_handle.extractall(CHOOSE_YOUR_PATH)
            zip_handle.close()
            return make_response("", 200)
        else:
            return make_response("", 400)
            
    

    ZipFile also has a contextmanager, if you prefer the with syntax.

    A word of caution: the _ in file._file indicates, that _file is not part of the public interface, and may change in future.