2011-03-23 13 views
13

Tengo una página que muestra una lista de archivos en un directorio. Cuando el usuario hace clic en el botón Descargar, todos estos archivos se comprimen en un solo archivo, que luego se ofrece para su descarga. Sé cómo enviar este archivo al navegador cuando se hace clic en el botón, y sé cómo volver a cargar la página actual (o redireccionar a otra), pero ¿es posible hacer ambas cosas en el mismo paso? ¿O tendría más sentido redirigir a una página diferente con un enlace de descarga?Método preferido para descargar un archivo generado sobre la marcha en Flask

Mi descarga se inicia con la API de Flask send_from_directory. código de prueba relevante:

@app.route('/download', methods=['GET','POST']) 
def download(): 
    error=None 
    # ... 

    if request.method == 'POST': 
     if download_list == None or len(download_list) < 1: 
      error = 'No files to download' 
     else: 
      timestamp = dt.now().strftime('%Y%m%d:%H%M%S') 
      zfname = 'reports-' + str(timestamp) + '.zip' 
      zf = zipfile.ZipFile(downloaddir + zfname, 'a') 
      for f in download_list: 
       zf.write(downloaddir + f, f) 
      zf.close() 

      # TODO: remove zipped files, move zip to archive 

      return send_from_directory(downloaddir, zfname, as_attachment=True) 

    return render_template('download.html', error=error, download_list=download_list) 

Actualización: Como solución, ahora estoy cargando una nueva página con el botón de clic, lo que permite al usuario iniciar la descarga (usando send_from_directory) antes de regresar a la lista actualizada.

Respuesta

7

¿Está ejecutando la aplicación del matraz detrás de un servidor web front-end como nginx o apache (que sería la mejor manera de manejar la descarga de archivos). Si está usando nginx, puede usar el encabezado 'X-Accel-Redirect'. Para este ejemplo, usaré el directorio /srv/static/reports como el directorio en el que está creando los archivos zip y quiero atenderlos.

nginx.conf

en la sección server

server { 
    # add this to your current server config 
    location /reports/ { 
    internal; 
    root /srv/static; 
    } 
} 

el método del frasco

enviar la cabecera de nginx al servidor

from flask import make_response 
@app.route('/download', methods=['GET','POST']) 
def download(): 
    error=None 
    # .. 
    if request.method == 'POST': 
     if download_list == None or len(download_list) < 1: 
      error = 'No files to download' 
      return render_template('download.html', error=error, download_list=download_list) 
     else: 
      timestamp = dt.now().strftime('%Y%m%d:%H%M%S') 
      zfname = 'reports-' + str(timestamp) + '.zip' 
      zf = zipfile.ZipFile(downloaddir + zfname, 'a') 
      for f in download_list: 
       zf.write(downloaddir + f, f) 
      zf.close() 

      # TODO: remove zipped files, move zip to archive 

      # tell nginx to server the file and where to find it 
      response = make_response() 
      response.headers['Cache-Control'] = 'no-cache' 
      response.headers['Content-Type'] = 'application/zip' 
      response.headers['X-Accel-Redirect'] = '/reports/' + zf.filename 
      return response 

Si está utilizando apache, puede usar su directiva sendfile http://httpd.apache.org/docs/2.0/mod/core.html#enablesendfile

+0

Gracias - la aplicación se ejecutará detrás de Apache, y veo que probablemente debería estar usando X-Sendfile para servir los archivos. La otra parte de mi pregunta era si es posible o no presentar una plantilla (para actualizar la información mostrada) y devolver una respuesta de archivo en el mismo paso. Mi comprensión hasta ahora es que no lo es. –

+0

¿Está utilizando POST una buena práctica para descargar? – Joe

Cuestiones relacionadas