I had previously answered a question to do something similar to this, although it seems to have been deleted. I came up with this gist which was themed around zipping up PIL images, but the concept is similar.
The objective was to build a ZIP file in memory, and serve with Flask without writing it to disc on the server.
To adapt this to your code you might make a function which processes ans
and returns a tuple, where the first item is the output path, and the second item is a BytesIO object.
import io, os
def process_individual(ans):
codepath = ans.find("a", {"class": "panel-group-toggle"}).text.strip().split("/") #File path and name
codeans = ans.find("code", {"class": "brush"}).text #The answer
# return a compatible tuple
return ( os.path.join(q, f'{exer_id}_template', *codepath),
io.BytesIO(codeans.encode(encoding='UTF-8') )
And define the business function which creates in the in memory zip, based on a list of tuples. This should take the filepath from the first item, and create that folder structure within the zip file accordingly.
import zipfile
def get_zip_buffer(list_of_tuples):
zip_buffer = io.BytesIO()
# https://stackoverflow.com/a/44946732 <3
with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED, False) as zip_file:
for file_name, data in list_of_tuples:
zip_file.writestr(file_name, data.read())
zip_buffer.seek(0)
return zip_buffer
To pull this together within Flask, you might have a download route like:
@app.route('/downloader')
def download():
list_of_tuples = [process_individual(a) for a in individual_ans]
buff = get_zip_buffer(list_of_tuples)
return send_file(buff,
mimetype='application/zip',
as_attachment=True,
attachment_filename='memoryzip.zip')
I haven't tested this with your input data, so it may need slight tweaking. Hopefully this helps.