Generando archivo para descargar con Django

96

¿Es posible crear un archivo zip y ofrecerlo para descargar, pero aún no guardar un archivo en el disco duro?

Josh Hunt
fuente

Respuestas:

111

Para activar una descarga, debe configurar el Content-Dispositionencabezado:

from django.http import HttpResponse
from wsgiref.util import FileWrapper

# generate the file
response = HttpResponse(FileWrapper(myfile.getvalue()), content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename=myfile.zip'
return response

Si no desea el archivo en el disco, debe usar StringIO

import cStringIO as StringIO

myfile = StringIO.StringIO()
while not_finished:
    # generate chunk
    myfile.write(chunk)

Opcionalmente, también puede configurar el Content-Lengthencabezado:

response['Content-Length'] = myfile.tell()
muhuk
fuente
1
Creo que Content-Length podría ocurrir automáticamente con el middleware de Django
andrewrk
4
Con este ejemplo, se descarga un archivo que siempre está vacío, ¿alguna idea?
camelCase
3
Como dijo @ eleaz28, en mi caso también estaba creando archivos en blanco. Acabo de quitar el FileWrappery funcionó.
Sébastien Deprez
Esta respuesta no funciona con Django 1.9: vea esto: stackoverflow.com/a/35485073/375966
Afshin Mehrabani
1
Abrí mi archivo en modo lectura y luego file.getvalue () está dando un error de atributo: TextIOWrapper no tiene atributo getValue.
Shubham Srivastava
26

Estarás más feliz creando un archivo temporal. Esto ahorra mucha memoria. Cuando tiene más de uno o dos usuarios al mismo tiempo, encontrará que el ahorro de memoria es muy, muy importante.

Sin embargo, puede escribir en un objeto StringIO .

>>> import zipfile
>>> import StringIO
>>> buffer= StringIO.StringIO()
>>> z= zipfile.ZipFile( buffer, "w" )
>>> z.write( "idletest" )
>>> z.close()
>>> len(buffer.getvalue())
778

El objeto "búfer" es similar a un archivo con un archivo ZIP de 778 bytes.

S.Lott
fuente
2
Buen punto sobre el ahorro de memoria. Pero si usa un archivo temporal, ¿dónde pondría el código para eliminarlo?
andrewrk
@ superjoe30: trabajos de limpieza periódicos. Django ya tiene un comando de administrador que debe ejecutarse periódicamente para eliminar sesiones antiguas.
S.Lott
@ superjoe30 para eso es / tmp :)
aehlke
@ S.Lott ¿Es posible servir el archivo creado (z en su ejemplo) usando mod x-sendfile?
miind
10

¿Por qué no crear un archivo tar en su lugar? Al igual que:

def downloadLogs(req, dir):
    response = HttpResponse(content_type='application/x-gzip')
    response['Content-Disposition'] = 'attachment; filename=download.tar.gz'
    tarred = tarfile.open(fileobj=response, mode='w:gz')
    tarred.add(dir)
    tarred.close()

    return response
Roshan
fuente
1
Para la versión más nueva de Django, debería tener en content_type=lugar demimetype=
Guillaume Lebreton
6

modelos.py

from django.db import models

class PageHeader(models.Model):
    image = models.ImageField(upload_to='uploads')

views.py

from django.http import HttpResponse
from StringIO import StringIO
from models import *
import os, mimetypes, urllib

def random_header_image(request):
    header = PageHeader.objects.order_by('?')[0]
    image = StringIO(file(header.image.path, "rb").read())
    mimetype = mimetypes.guess_type(os.path.basename(header.image.name))[0]

    return HttpResponse(image.read(), mimetype=mimetype)
Ryan Anguiano
fuente
No parece seguro crear una cadena en memoria del tamaño de la imagen.
dhill
5
def download_zip(request,file_name):
    filePath = '<path>/'+file_name
    fsock = open(file_name_with_path,"rb")
    response = HttpResponse(fsock, content_type='application/zip')
    response['Content-Disposition'] = 'attachment; filename=myfile.zip'
    return response

Puede reemplazar el código postal y el tipo de contenido según sus requisitos.

Kamesh Jungi
fuente
1
fsock = open(filePath,"rb")
Querías
4

Lo mismo con el archivo tgz en memoria:

import tarfile
from io import BytesIO


def serve_file(request):
    out = BytesIO()
    tar = tarfile.open(mode = "w:gz", fileobj = out)
    data = 'lala'.encode('utf-8')
    file = BytesIO(data)
    info = tarfile.TarInfo(name="1.txt")
    info.size = len(data)
    tar.addfile(tarinfo=info, fileobj=file)
    tar.close()

    response = HttpResponse(out.getvalue(), content_type='application/tgz')
    response['Content-Disposition'] = 'attachment; filename=myfile.tgz'
    return response
scythargon
fuente