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.