Generieren einer Datei zum Herunterladen mit Django

96

Ist es möglich, ein Zip-Archiv zu erstellen und zum Herunterladen anzubieten, aber dennoch keine Datei auf der Festplatte zu speichern?

Josh Hunt
quelle

Antworten:

111

Um einen Download auszulösen, müssen Sie den Content-DispositionHeader festlegen :

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

Wenn Sie die Datei nicht auf der Festplatte haben möchten, müssen Sie sie verwenden StringIO

import cStringIO as StringIO

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

Optional können Sie auch den Content-LengthHeader festlegen :

response['Content-Length'] = myfile.tell()
Muhuk
quelle
1
Ich denke, Content-Length könnte automatisch mit Django Middleware passieren
andrewrk
4
Anhand dieses Beispiels wird eine Datei heruntergeladen, die immer leer ist. Irgendwelche Ideen?
camelCase
3
Wie @ eleaz28 sagte, wurden auch in meinem Fall leere Dateien erstellt. Ich habe das gerade entfernt FileWrapperund es hat funktioniert.
Sébastien Deprez
Diese Antwort funktioniert nicht mit Django 1.9: siehe dies: stackoverflow.com/a/35485073/375966
Afshin Mehrabani
1
Ich habe meine Datei im Lesemodus geöffnet, dann gibt file.getvalue () einen Attributfehler aus: TextIOWrapper hat kein Attribut getValue.
Shubham Srivastava
26

Sie werden glücklicher sein, eine temporäre Datei zu erstellen. Das spart viel Speicher. Wenn Sie mehr als einen oder zwei Benutzer gleichzeitig haben, ist die Speichereinsparung sehr, sehr wichtig.

Sie können jedoch in ein StringIO- Objekt schreiben .

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

Das "Puffer" -Objekt ist dateiähnlich mit einem 778-Byte-ZIP-Archiv.

S.Lott
quelle
2
Guter Punkt zum Speichern von Speicher. Aber wenn Sie eine temporäre Datei verwenden, wo würden Sie den Code ablegen, um sie zu löschen?
Andrewrk
@ superjoe30: regelmäßige Bereinigungsjobs. Django verfügt bereits über einen Administratorbefehl, der regelmäßig ausgeführt werden muss, um alte Sitzungen zu entfernen.
S.Lott
@ Superjoe30 das ist, was / tmp ist für :)
Aehlke
@ S.Lott Ist es möglich, die erstellte Datei (in Ihrem Beispiel z in) mit mod x-sendfile bereitzustellen?
Miind
10

Warum nicht stattdessen eine TAR-Datei erstellen? Wie so:

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
quelle
1
Für eine neuere Version von Django sollten Sie content_type=anstelle vonmimetype=
Guillaume Lebreton
9

Ja, Sie können das zipfile-Modul , das zlib-Modul oder andere Komprimierungsmodule verwenden , um ein zip-Archiv im Speicher zu erstellen. Sie können Ihre Ansicht veranlassen, das Zip-Archiv in das HttpResponseObjekt zu schreiben, das die Django-Ansicht zurückgibt, anstatt einen Kontext an eine Vorlage zu senden. Zuletzt müssen Sie den Mimetyp auf das entsprechende Format einstellen , damit der Browser die Antwort als Datei behandelt .

Soviut
quelle
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
quelle
Sieht unsicher aus, um eine speicherinterne Zeichenfolge mit Bildgröße zu erstellen.
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

Sie können die Postleitzahl und den Inhaltstyp gemäß Ihren Anforderungen ersetzen.

Kamesh Jungi
quelle
1
Du fsock = open(filePath,"rb")
meintest
4

Gleiches gilt für das tgz-Archiv im Speicher:

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
quelle