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?

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()

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.


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

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)

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.


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.