Sometimes it is necessary to combine files into a zip without actually storing them. One example could be to put a screenshot along with some legal text.
In order to achieve this in python
from io import BytesIO
from zipfile import ZipFile
def create_archive(file_name: str, content: bytes) -> ZipFile:
archive = BytesIO()
with ZipFile(archive, 'w') as zip_file:
with zip_file.open(file_name, 'w') as f:
f.write(content)
return zip_file
archive
can be returned via flask with a specified name:
from io import BytesIO
from zipfile import ZipFile
from flask import Flask, Response
app = Flask(__name__)
def _create_archive(file_name: str, content: bytes) -> BytesIO:
archive = BytesIO()
with ZipFile(archive, 'w') as zip_file:
with zip_file.open(file_name, 'w') as f:
f.write(content)
return archive
@app.route("/")
def hello_world():
archive: BytesIO = _create_archive("legal.txt", b"This is the legal text")
response = Response(archive.getvalue(), mimetype="application/zip")
response.headers.set("Content-Disposition", "attachment", filename="archive.zip")
return response
If you want to download an extra file and include it in the zip file, you need to take care of getting the name of the file. Name of the file may contain url encoded characters such as %20
for space.
from pathlib import Path
from urllib.parse import unquote, urlparse
url = "https://someurl/some%20picture.jpg?trackid=sometrackingid"
# Use urlparse so that the url parameters could be ignored
print(urlparse(url).path)
# > /some%20picture.jpg
# Use Path to get the name of the file without the directory dividers
print(Path(urlparse(url).path).name)
# > some%20picture.jpg
# Use unquote to get rid of the url encoding
print(unquote(Path(urlparse(url).path).name))
# > some picture.jpg
Here is the full code:
from io import BytesIO
from pathlib import Path
from urllib.parse import unquote, urlparse
from zipfile import ZipFile
from flask import Flask, Response
import requests
app = Flask(__name__)
def _create_archive(file_name: str, content: bytes) -> BytesIO:
archive = BytesIO()
url = "https://someurl/some%20picture.jpg?trackid=sometrackingid"
response = requests.get(url)
file_name = unquote(Path(urlparse(url).path).name)
with ZipFile(archive, "w") as zip_file:
with zip_file.open(file_name, "w") as f:
f.write(content)
with zip_file.open(file_name, "w") as f:
f.write(response.content)
return archive
@app.route("/")
def hello_world():
archive = _create_zip_archive()
response = Response(archive.getvalue(), mimetype="application/zip")
response.headers.set("Content-Disposition", "attachment", filename="archive.zip")
return response