Django提供的文件为空

2 投票
2 回答
2097 浏览
提问于 2025-04-17 09:35

我有一段代码是用来通过Django管理文件下载的。

def serve_file(request, id):

    file = models.X.objects.get(id=id).file #FileField
    file.open('rb')
    wrapper = FileWrapper(file)
    mt = mimetypes.guess_type(file.name)[0]
    response = HttpResponse(wrapper, content_type=mt)

    import unicodedata, os.path
    filename = unicodedata.normalize('NFKD', os.path.basename(file.name)).encode("utf8",'ignore')
    filename = filename.replace(' ', '-') #Avoid browser to ignore any char after the space

    response['Content-Length'] = file.size
    response['Content-Disposition'] = 'attachment; filename={0}'.format(filename)

    #print response
    return response

可惜的是,我的浏览器下载时得到的是一个空文件。

打印出来的响应看起来是正确的:

Content-Length: 3906
Content-Type: text/plain
Content-Disposition: attachment; filename=toto.txt

blah blah ....

我有类似的代码运行得很好。我看不出问题出在哪里。有什么想法吗?

附注:我测试了这里提出的解决方案,结果也是一样。

更新:把 wrapper = FileWrapper(file) 替换成 wrapper = file.read() 似乎解决了这个问题。

更新:如果我注释掉 print response,我也遇到了类似的问题:文件是空的。唯一的不同是:Firefox检测到文件大小为20字节。(而这个文件其实比这大)

2 个回答

0

来自 Django文档

FieldFile.open(mode='rb') 的功能和标准的Python open() 方法一样,可以根据指定的模式打开与这个实例关联的文件。

如果它的工作方式和Python的 open 一样,那么它应该返回一个文件对象,使用方法如下:

f = file.open('rb')
wrapper = FileWrapper(f)
1

文件对象是一种可以遍历的对象,也可以看作是一个生成器。它只能被读取一次,读取完后就用完了。如果你想再读取,就得重新创建一个新的文件对象,或者使用某个方法让它从头开始读取(比如:seek())。

read() 方法会返回一个字符串,这个字符串可以多次读取而不会有问题,这就是它能解决你问题的原因。

所以要注意,如果你使用的是像文件这样的对象,尽量不要连续读取两次。例如:不要先打印它,然后再返回它。

撰写回答