生成文件以使用Django下载


Answers:


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 2010年

4
使用此示例将下载一个始终为空的文件,有什么想法吗?
camelCase

3
正如@ eleaz28所说,就我而言,它也在创建空白文件。我刚刚删除了FileWrapper,它成功了。
塞巴斯蒂安Deprez

这个答案不适用于Django 1.9:请参阅:stackoverflow.com/a/35485073/375966
Afshin Mehrabani

1
我以读取模式打开文件,然后file.getvalue()给出属性错误:TextIOWrapper没有属性getValue。
Shubham Srivastava '18

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 2010年

@ superjoe30:定期清理作业。Django已经有一个admin命令,必须定期运行该命令才能删除旧会话。
S.Lott

@ superjoe30就是/ tmp的意思:)
aehlke

@ S.Lott是否可以使用mod x-sendfile服务创建的文件(在您的示例中为z)?
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=
纪尧姆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)

创建图像大小的内存字符串看起来不安全。
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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.