python - descargar - django windows
Sirviendo archivos ZIP generados dinĂ¡micamente en Django (9)
¿Cómo servir a los usuarios un archivo ZIP generado dinámicamente en Django?
Estoy creando un sitio, donde los usuarios pueden elegir cualquier combinación de libros disponibles y descargarlos como archivo ZIP. Me preocupa que la generación de dichos archivos para cada solicitud reduzca la velocidad de mi servidor. También he escuchado que Django actualmente no tiene una buena solución para servir archivos generados dinámicamente.
¿No puedes simplemente escribir un enlace a un "servidor zip" o algo así? ¿Por qué el archivo comprimido en sí necesita ser servido desde Django? Una secuencia de comandos CGI de la era de los 90 para generar un zip y escupirlo a stdout es realmente todo lo que se necesita aquí, al menos hasta donde puedo ver.
Aquí hay una vista de Django para hacer esto:
import os
import zipfile
import StringIO
from django.http import HttpResponse
def getfiles(request):
# Files (local path) to put in the .zip
# FIXME: Change this (get paths from DB etc)
filenames = ["/tmp/file1.txt", "/tmp/file2.txt"]
# Folder name in ZIP archive which contains the above files
# E.g [thearchive.zip]/somefiles/file2.txt
# FIXME: Set this to something better
zip_subdir = "somefiles"
zip_filename = "%s.zip" % zip_subdir
# Open StringIO to grab in-memory ZIP contents
s = StringIO.StringIO()
# The zip compressor
zf = zipfile.ZipFile(s, "w")
for fpath in filenames:
# Calculate path for file in zip
fdir, fname = os.path.split(fpath)
zip_path = os.path.join(zip_subdir, fname)
# Add file, at correct path
zf.write(fpath, zip_path)
# Must close zip for all contents to be written
zf.close()
# Grab ZIP file from in-memory, make response with correct MIME-type
resp = HttpResponse(s.getvalue(), mimetype = "application/x-zip-compressed")
# ..and correct content-disposition
resp[''Content-Disposition''] = ''attachment; filename=%s'' % zip_filename
return resp
Django no maneja directamente la generación de contenido dinámico (específicamente archivos Zip). Ese trabajo lo haría la biblioteca estándar de Python. Puede ver cómo crear dinámicamente un archivo Zip en Python here .
Si le preocupa que disminuya la velocidad de su servidor, puede almacenar en caché las solicitudes si espera tener muchas de las mismas solicitudes. Puede usar el marco de caché de Django para ayudarlo con eso.
En general, los archivos comprimidos pueden consumir mucha CPU, pero Django no debe ser más lento que otro framework web de Python.
Enchufe desvergonzado: puede usar django-zipview para el mismo propósito.
Después de un pip install django-zipview
:
from zipview.views import BaseZipView
from reviews import Review
class CommentsArchiveView(BaseZipView):
"""Download at once all comments for a review."""
def get_files(self):
document_key = self.kwargs.get(''document_key'')
reviews = Review.objects /
.filter(document__document_key=document_key) /
.exclude(comments__isnull=True)
return [review.comments.file for review in reviews if review.comments.name]
Este módulo genera y transmite un archivo: https://github.com/allanlei/python-zipstream
(No estoy conectado al desarrollo. Solo estoy pensando en usarlo).
La solución es la siguiente.
Utilice el archivo zip del módulo Python para crear un archivo zip, pero como el archivo especifica el objeto StringIO (el constructor ZipFile requiere un objeto similar a un archivo). Agregue los archivos que desea comprimir. Luego, en su aplicación Django, devuelva el contenido del objeto StringIO en HttpResponse
con mimetype establecido en application/x-zip-compressed
(o al menos application/octet-stream
). Si lo desea, puede configurar el encabezado content-disposition
, pero esto no debería ser realmente necesario.
Pero cuidado, crear archivos zip en cada solicitud es mala idea y esto puede matar tu servidor (sin contar los tiempos de espera si los archivos son grandes). El enfoque basado en el rendimiento consiste en almacenar en caché la salida generada en algún lugar del sistema de archivos y regenerarla solo si los archivos fuente han cambiado. Una idea aún mejor es preparar los archivos por adelantado (por ejemplo, mediante un trabajo cron) y hacer que su servidor web los sirva como estáticos habituales.
Muchas respuestas aquí sugieren usar un StringIO
o BytesIO
. Sin embargo, esto no es necesario ya que HttpResponse
ya es un objeto similar a un archivo:
response = HttpResponse(content_type=''application/zip'')
zip_file = zipfile.ZipFile(response, ''w'')
for filename in filenames:
zip_file.write(filename)
response[''Content-Disposition''] = ''attachment; filename={}''.format(zipfile_name)
return response
Para python3 utilizo el io.ByteIO ya que StringIO está en desuso para lograr esto. Espero eso ayude.
import io
def my_downloadable_zip(request):
zip_io = io.BytesIO()
with zipfile.ZipFile(zip_io, mode=''w'', compression=zipfile.ZIP_DEFLATED) as backup_zip:
backup_zip.write(''file_name_loc_to_zip'') # u can also make use of list of filename location
# and do some iteration over it
response = HttpResponse(zip_io.getvalue(), content_type=''application/x-zip-compressed'')
response[''Content-Disposition''] = ''attachment; filename=%s'' % ''your_zipfilename'' + ".zip"
response[''Content-Length''] = zip_io.tell()
return response
Sugiero usar un modelo separado para almacenar esos archivos zip temporales. Puede crear zip on-fly, guardar en el modelo con el campo de archivos y finalmente enviar url al usuario.
Ventajas:
- Sirviendo archivos zip estáticos con el mecanismo de medios django (como cargas usuales).
- Posibilidad de limpiar archivos zip obsoletos mediante la ejecución regular de scripts cron (que puede usar el campo de fecha desde el modelo de archivo zip).