En mi proyecto, cuando un usuario hace clic en un enlace, una solicitud de AJAX envía la información necesaria para crear un CSV. El CSV tarda mucho en generarse, por lo que quiero poder incluir un enlace de descarga para el CSV generado en la respuesta de AJAX. es posible?
La mayoría de las respuestas que he visto devuelven el CSV de la siguiente manera:
return Response( csv, mimetype="text/csv", headers={"Content-disposition": "attachment; filename=myplot.csv"})Sin embargo, no creo que esto sea compatible con la respuesta AJAX que estoy enviando con:
return render_json(200, {'data': params})Idealmente, me gustaría poder enviar el enlace de descarga en el dictado de parámetros. Pero tampoco estoy seguro de si esto es seguro. ¿Cómo se suele resolver este problema?
Creo que una solución puede ser la biblioteca de futures ( pip install futures ). El primer punto final puede poner en cola la tarea y luego devolver el nombre del archivo, y luego se puede usar otro punto final para recuperar el archivo. También gzip porque podría ser una buena idea si envías archivos más grandes. Creo que las soluciones más robustas usan Celery o Rabbit MQ o algo por el estilo. Sin embargo, esta es una solución simple que debería lograr lo que está pidiendo.
from flask import Flask, jsonify, Response from uuid import uuid4 from concurrent.futures import ThreadPoolExecutor import time import os import gzip app = Flask(__name__) # Global variables used by the thread executor, and the thread executor itself NUM_THREADS = 5 EXECUTOR = ThreadPoolExecutor(NUM_THREADS) OUTPUT_DIR = os.path.dirname(os.path.abspath(__file__)) # this is your long running processing function # takes in your arguments from the /queue-task endpoint def a_long_running_task(*args): time_to_wait, output_file_name = int(args[0][0]), args[0][1] output_string = 'sleeping for {0} seconds. File: {1}'.format(time_to_wait, output_file_name) print(output_string) time.sleep(time_to_wait) filename = os.path.join(OUTPUT_DIR, output_file_name) # here we are writing to a gzipped file to save space and decrease size of file to be sent on network with gzip.open(filename, 'wb') as f: f.write(output_string) print('finished writing {0} after {1} seconds'.format(output_file_name, time_to_wait)) # This is a route that starts the task and then gives them the file name for reference @app.route('/queue-task/<wait>') def queue_task(wait): output_file_name = str(uuid4()) + '.csv' EXECUTOR.submit(a_long_running_task, [wait, output_file_name]) return jsonify({'filename': output_file_name}) # this takes the file name and returns if exists, otherwise notifies it is not yet done @app.route('/getfile/<name>') def get_output_file(name): file_name = os.path.join(OUTPUT_DIR, name) if not os.path.isfile(file_name): return jsonify({"message": "still processing"}) # read without gzip.open to keep it compressed with open(file_name, 'rb') as f: resp = Response(f.read()) # set headers to tell encoding and to send as an attachment resp.headers["Content-Encoding"] = 'gzip' resp.headers["Content-Disposition"] = "attachment; filename={0}".format(name) resp.headers["Content-type"] = "text/csv" return resp if __name__ == '__main__': app.run()