Menghasilkan file untuk diunduh dengan Django

96

Apakah mungkin membuat arsip zip dan menawarkannya untuk diunduh, tetapi tetap tidak menyimpan file ke hard drive?

Josh Hunt
sumber

Jawaban:

111

Untuk memicu unduhan, Anda perlu menyetel Content-Dispositionheader:

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

Jika Anda tidak ingin file pada disk Anda perlu menggunakan StringIO

import cStringIO as StringIO

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

Secara opsional, Anda juga dapat mengatur Content-Lengthtajuk:

response['Content-Length'] = myfile.tell()
muhuk
sumber
1
Saya pikir Panjang Konten mungkin terjadi secara otomatis dengan middleware Django
andrewrk
4
Menggunakan contoh ini mengunduh file yang selalu kosong, ada ide?
CamelCase
3
Seperti yang dikatakan @ eleaz28, itu juga membuat file kosong dalam kasus saya. Saya baru saja menghapusnya FileWrapper, dan berhasil.
Sébastien Deprez
Jawaban ini tidak bekerja dengan Django 1.9: lihat ini: stackoverflow.com/a/35485073/375966
Afshin Mehrabani
1
Saya membuka file saya dalam mode baca kemudian file.getvalue () memberikan kesalahan atribut: TextIOWrapper tidak memiliki atribut getValue.
Shubham Srivastava
26

Anda akan lebih senang membuat file sementara. Ini menghemat banyak memori. Bila Anda memiliki lebih dari satu atau dua pengguna secara bersamaan, Anda akan menemukan bahwa penyimpanan memori sangat, sangat penting.

Namun, Anda dapat menulis ke objek StringIO .

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

Objek "buffer" adalah seperti file dengan arsip ZIP 778 byte.

S. Lott
sumber
2
Poin bagus tentang menghemat memori. Tetapi jika menggunakan file sementara, di mana Anda akan meletakkan kode untuk menghapusnya?
andrewrk
@ superjoe30: pekerjaan pembersihan berkala. Django sudah mempunyai perintah admin yang harus dijalankan secara berkala untuk menghapus sesi lama.
S. Lott
@ superjoe30 untuk itulah / tmp :)
aehlke
@ S.Lott Apakah mungkin untuk menyajikan file yang dibuat (z dalam contoh Anda) menggunakan mod x-sendfile?
Miind
10

Mengapa tidak membuat file tar saja? Seperti:

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
sumber
1
Untuk versi yang lebih baru dari Django, Anda harus memiliki content_type=bukannyamimetype=
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
sumber
Tampaknya tidak aman membuat string ukuran gambar dalam memori.
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

Anda dapat mengganti zip dan tipe konten sesuai kebutuhan Anda.

Kamesh Jungi
sumber
1
Maksud Andafsock = open(filePath,"rb")
stelios
4

Sama dengan di arsip tgz memori:

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
sabit besar
sumber