我在项目根文件夹中有一些图片,我想让它们可作为zip存档动态下载。
所有图片都具有相同的名称,但是结尾处是序列号,所以我尝试这样做
def zip_files(name, iterat):
temp = tempfile.TemporaryFile()
archive = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED)
for index in range(iterat):
filename = name+"_"+str(index)+".jpg"
archive.write(filename)
archive.close()
wrapper = FileWrapper(temp)
response = HttpResponse(wrapper, content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename=test.zip'
response['Content-Length'] = temp.tell()
temp.seek(0)
return response
所以我在第response['Content-Length'] = temp.tell()
和temp.seek(0)
行出现了错误
已关闭文件中的操作。
当我注释这些行时,返回给ajax的数据为空(因为这是作为ajax请求触发的)
更新
我使用NamedTemporaryFile
如下:
def zip_files(name, iterat):
temp = tempfile.NamedTemporaryFile(delete=False)
archive = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED)
for index in range(iterat):
filename = name+"_"+str(index)+".jpg"
archive.write(filename)
archive.close()
wrapper = FileWrapper(temp)
response = HttpResponse(wrapper, content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename=test.zip'
archive = zipfile.ZipFile(temp.name, 'r')
response['Content-Length'] = open(temp.name).tell()
return response
现在我在服务器端没有错误,但是返回给ajax请求的数据仍然为空,在浏览器网络标签中,所有添加到HttpResponse
的信息都在响应标头中,如下所示:
Content-Disposition: attachment; filename=test.zip
Content-Length: 0
Content-Type: application/zip
Date: Wed, 27 Mar 2019 15:32:08 GMT
Server: WSGIServer/0.2 CPython/3.7.2
X-Frame-Options: SAMEORIGIN
答案 0 :(得分:1)
对tempfile.TemporaryFile()
的调用返回文件句柄,而不是文件名。
这将关闭文件句柄:
archive.close()
之后,将无法再使用手柄。实际上,将通过关闭文件https://docs.python.org/3/library/tempfile.html#tempfile.TemporaryFile
从磁盘上删除该文件。因此,即使您可以询问tempfile.TemporaryFile()
的结果作为名称,也无济于事。
您需要做的是请求一个临时的文件名(而不只是文件名)。然后为此文件名创建一个句柄,写入数据,关闭该句柄。对于请求,使用名称创建一个新的文件句柄。
方法tempfile.NamedTemporaryFile()
应该适合您。确保您通过了选项delete=False
。您可以从temp.name
获取文件的路径。参见https://docs.python.org/3/library/tempfile.html#tempfile.NamedTemporaryFile
发送响应后,这会将文件保留在磁盘上。要解决此问题,请扩展FileWrapper
并覆盖close()
方法:
class DeletingFileWrapper(FileWrapper):
def close(self):
# First close the file handle to avoid errors when deleting the file
super(DeletingFileWrapper,self).close()
os.remove(self.filelike.name)
如果ZIP文件很大,您还希望使用StreamingHttpResponse
而不是HttpResponse
,因为后者会立即将整个文件读入内存。
更新
您仍在使用非法(关闭)文件句柄:FileWrapper(temp)
正确的代码为:
wrapper = DeletingFileWrapper(open(temp.name, 'b'))
并且您需要使用采用文件名来确定长度的方法,因为open(temp.name).tell()
始终返回0。请检查os
模块。
另请参阅: