Генерування файлу для завантаження за допомогою Django


96

Чи можна створити zip-архів і запропонувати його завантажити, але при цьому не зберегти файл на жорсткий диск?

Відповіді:


111

Для запуску завантаження потрібно встановити Content-Dispositionзаголовок:

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

Якщо ви не хочете файл на диску, вам потрібно скористатися StringIO

import cStringIO as StringIO

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

За бажанням ви також можете встановити Content-Lengthзаголовок:

response['Content-Length'] = myfile.tell()

1
Я думаю, що Content-Length може відбуватися автоматично з проміжним програмним забезпеченням Django
andrewrk

4
За допомогою цього прикладу завантажується файл, який завжди порожній, є ідеї?
camelCase

3
Як сказав @ eleaz28, у моєму випадку це також створювало порожні файли. Я щойно видалив FileWrapper, і це спрацювало.
Себастьян Депре

Ця відповідь не працює з Django 1.9: подивитися: stackoverflow.com/a/35485073/375966
Afshin Mehrabani

1
Я відкрив свій файл у режимі читання, тоді file.getvalue () видає помилку атрибута: TextIOWrapper не має атрибута getValue.
Шубхам Шрівастава,

26

Вам буде приємніше створювати тимчасовий файл. Це економить багато пам’яті. Якщо одночасно у вас є більше одного або двох користувачів, ви виявите, що економія пам'яті дуже, дуже важлива.

Тим не менш, ти можеш написати в об'єкт StringIO .

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

Об'єкт "буфер" схожий на файл із 778-байтовим архівом ZIP.


2
Хороший момент щодо економії пам’яті. Але якщо ви використовуєте тимчасовий файл, куди б ви поклали код для його видалення?
andrewrk

@ superjoe30: періодичні роботи з очищення. Django вже має команду адміністратора, яку потрібно періодично запускати, щоб видалити старі сеанси.
S.Lott,

@ superjoe30 ось для чого / tmp :)
aehlke

@ S.Lott Чи можна обслуговувати створений файл (z у вашому прикладі) за допомогою mod x-sendfile?
Miind

10

Чому б замість цього не створити файл tar? Так:

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
Для нової версії Django вам слід було б content_type=замістьmimetype=
Гійома Лебретона

9

Так, ви можете використовувати ZipFile модуль , модуль ZLIB або інші модулі стиснення для створення архіву поштового індексу в пам'яті. Ви можете змусити свій вигляд писати zip-архів до HttpResponseоб’єкта, який повертає подання Django, замість того, щоб надсилати контекст шаблону. Нарешті, вам потрібно буде встановити mimeype у відповідному форматі, щоб повідомити браузеру обробляти відповідь як файл .


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)

Небезпечно створювати рядок розміру зображення в пам'яті.
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

Ви можете замінити zip та тип вмісту відповідно до ваших вимог.


1
Ви мали на увазіfsock = open(filePath,"rb")
stelios

4

Те саме з архівом tgz в пам'яті:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.