Создание файла для загрузки с помощью 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
Афшин Мехрабани
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

Объект "буфер" подобен файлу с архивом ZIP 778 байт.

С.Лотт
источник
2
Хороший момент по поводу экономии памяти. Но если вы используете временный файл, где бы вы поместили код для его удаления?
andrewrk
@ superjoe30: периодические работы по очистке. В Django уже есть команда администратора, которую необходимо периодически запускать для удаления старых сеансов.
S.Lott
@ superjoe30 вот для чего нужен / tmp :)
aehlke
@ S.Lott Можно ли обслужить созданный файл (z in в вашем примере) с помощью мода 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=
Guillaume Lebreton
9

Да, вы можете использовать ZipFile модуль , модуль ZLIB или другие модули сжатия для создания архива почтового индекса в памяти. Вы можете заставить ваше представление записывать zip-архив в HttpResponseобъект, который возвращает представление Django, вместо отправки контекста в шаблон. Наконец, вам нужно установить для mimetype соответствующий формат, чтобы браузер обрабатывал ответ как файл .

Совиут
источник
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")
Стелиос
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
сцифаргон
источник