Gerando arquivo para download com Django


96

É possível fazer um arquivo zip e disponibilizá-lo para download, mas ainda não salvar um arquivo no disco rígido?

Respostas:


111

Para acionar um download, você precisa definir o Content-Dispositioncabeçalho:

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

Se você não quiser o arquivo no disco, você precisa usar StringIO

import cStringIO as StringIO

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

Opcionalmente, você também pode definir o Content-Lengthcabeçalho:

response['Content-Length'] = myfile.tell()

1
Acho que o Content-Length pode acontecer automaticamente com o Django middleware
andrewrk

4
Usando este exemplo baixa um arquivo que está sempre vazio, alguma ideia?
camelCase de

3
Como @ eleaz28 disse, estava criando arquivos em branco no meu caso também. Acabei de remover o FileWrappere funcionou.
Sébastien Deprez

Esta resposta não funciona com Django 1.9: veja isto: stackoverflow.com/a/35485073/375966
Afshin Mehrabani

1
Eu abri meu arquivo em modo de leitura, então file.getvalue () está apresentando um erro de atributo: TextIOWrapper não tem atributo getValue.
Shubham Srivastava

26

Você ficará mais feliz criando um arquivo temporário. Isso economiza muita memória. Quando você tem mais de um ou dois usuários simultaneamente, descobrirá que a economia de memória é muito, muito importante.

Você pode, no entanto, gravar em um objeto StringIO .

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

O objeto "buffer" é semelhante a um arquivo com um arquivo ZIP de 778 bytes.


2
Bom ponto sobre como economizar memória. Mas se estiver usando um arquivo temporário, onde você colocaria o código para excluí-lo?
andrewrk

@ superjoe30: trabalhos de limpeza periódicos. Django já possui um comando admin que deve ser executado periodicamente para remover sessões antigas.
S.Lott

@ superjoe30 é para isso que serve / tmp :)
aehlke

@ S.Lott É possível servir o arquivo criado (z em seu exemplo) usando mod x-sendfile?
Miind

10

Por que não criar um arquivo tar? Igual a:

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

1
Para uma versão mais recente do Django, você deve ter em content_type=vez demimetype=
Guillaume Lebreton


6

models.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)

Parece inseguro criar uma string de tamanho de imagem na memória.
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

Você pode substituir o zip e o tipo de conteúdo de acordo com sua necessidade.


1
Você quis dizerfsock = open(filePath,"rb")
stelios

4

Mesmo com o arquivo tgz na memória:

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.