Generowanie pliku do pobrania za pomocą Django

96

Czy można utworzyć archiwum zip i zaoferować je do pobrania, ale nadal nie można zapisać pliku na dysku twardym?

Josh Hunt
źródło

Odpowiedzi:

111

Aby uruchomić pobieranie, musisz ustawić Content-Dispositionnagłówek:

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

Jeśli nie chcesz pliku na dysku, musisz użyć StringIO

import cStringIO as StringIO

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

Opcjonalnie możesz również ustawić Content-Lengthnagłówek:

response['Content-Length'] = myfile.tell()
muhuk
źródło
1
Myślę, że Content-Length może nastąpić automatycznie z oprogramowaniem pośredniczącym Django
andrewrk
4
Korzystając z tego przykładu, pobiera plik, który jest zawsze pusty. Masz jakieś pomysły?
sprawa camelCase
3
Jak powiedział @ eleaz28, w moim przypadku również tworzyło puste pliki. Właśnie usunąłem FileWrapperi zadziałało.
Sébastien Deprez
Ta odpowiedź nie działa z Django 1.9: zobacz to: stackoverflow.com/a/35485073/375966
Afshin Mehrabani
1
Otworzyłem plik w trybie odczytu, a następnie file.getvalue () wyświetla błąd atrybutu: TextIOWrapper nie ma atrybutu getValue.
Shubham Srivastava
26

Będziesz szczęśliwszy, tworząc plik tymczasowy. Oszczędza to dużo pamięci. Kiedy masz więcej niż jednego lub dwóch użytkowników jednocześnie, zauważysz, że oszczędzanie pamięci jest bardzo, bardzo ważne.

Możesz jednak pisać do obiektu StringIO .

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

Obiekt "bufor" jest podobny do pliku z 778-bajtowym archiwum ZIP.

S.Lott
źródło
2
Dobra uwaga na temat oszczędzania pamięci. Ale jeśli używasz pliku tymczasowego, gdzie umieściłbyś kod, aby go usunąć?
andrewrk
@ superjoe30: okresowe prace porządkowe. Django ma już polecenie administratora, które musi być uruchamiane okresowo, aby usunąć stare sesje.
S.Lott
@ superjoe30 po to jest / tmp :)
aehlke
@ S.Lott Czy można obsłużyć utworzony plik (w twoim przykładzie z) za pomocą mod x-sendfile?
Miind
10

Dlaczego zamiast tego nie utworzyć pliku tar? Tak jak to:

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
źródło
1
W przypadku nowszej wersji Django powinieneś content_type=zamiast tegomimetype=
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)
Ryan Anguiano
źródło
Tworzenie w pamięci ciągu o rozmiarze obrazu wydaje się niebezpieczne.
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

Możesz zastąpić kod pocztowy i typ zawartości zgodnie z wymaganiami.

Kamesh Jungi
źródło
1
Miałeś na myślifsock = open(filePath,"rb")
stelios
4

To samo z archiwum tgz w pamięci:

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
źródło