[英]Download some files as one zipped archive
i have some pictures in project root folder and i want to make them available to download dynamically as zip archive. 我在项目根文件夹中有一些图片,我想使它们可作为zip存档动态下载。
All the pictures have same name but the difference is in sequence number at the end, So i tried to do this 所有图片都具有相同的名称,但最后是序列号不同,所以我尝试这样做
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
so i got errors in lines response['Content-Length'] = temp.tell()
and temp.seek(0)
所以我
response['Content-Length'] = temp.tell()
和temp.seek(0)
Operation in closed file.
在关闭文件中操作。
and when i comment those lines, the returned data to the ajax is empty (because this is triggered as an ajax request) 当我注释这些行时,返回给ajax的数据为空(因为这是作为ajax请求触发的)
Update 更新
I used the NamedTemporaryFile
as following : 我使用了
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
now i have no errors in server side but the returned data to the ajax request still empty, in browser network tab all information added to HttpResponse
are in the Response Headers as following: 现在我在服务器端没有错误,但是返回给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
The call to tempfile.TemporaryFile()
returns a file handle, not a file name. 调用
tempfile.TemporaryFile()
返回文件句柄,而不是文件名。
This will close the file handle: 这将关闭文件句柄:
archive.close()
Afterwards, the handle can't be used anymore. 之后,将无法再使用手柄。 In fact, the file will be deleted from disk by closing it: https://docs.python.org/3/library/tempfile.html#tempfile.TemporaryFile
实际上,将通过关闭文件将其从磁盘上删除: https : //docs.python.org/3/library/tempfile.html#tempfile.TemporaryFile
So even if you could ask the result of tempfile.TemporaryFile()
for its name, that wouldn't help. 因此,即使您可以向
tempfile.TemporaryFile()
的结果询问其名称,也无济于事。
What you need to do is ask for a temporary file name (instead of just the file). 您需要做的是请求一个临时文件名 (而不只是文件名)。 Then create a handle for this file name, write the data, close the handle.
然后为此文件名创建一个句柄,写入数据,关闭该句柄。 For the request, create a new file handle using the name.
对于该请求,使用名称创建一个新的文件句柄。
The method tempfile.NamedTemporaryFile()
should work for you. 方法
tempfile.NamedTemporaryFile()
应该适合您。 Make sure you pass the option delete=False
. 确保传递选项
delete=False
。 You can get the path to the file from temp.name
. 您可以从
temp.name
获取文件的路径。 See https://docs.python.org/3/library/tempfile.html#tempfile.NamedTemporaryFile 参见https://docs.python.org/3/library/tempfile.html#tempfile.NamedTemporaryFile
This will leave the file on disk after the response has been sent. 发送响应后,这会将文件保留在磁盘上。 To fix this, extend the
FileWrapper
and overwrite the close()
method: 要解决此问题,请扩展
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)
If the ZIP file is big, you also want to use StreamingHttpResponse
instead of HttpResponse
since the latter will read the whole file into memory at once. 如果ZIP文件很大,您还希望使用
StreamingHttpResponse
而不是HttpResponse
因为后者会立即将整个文件读入内存。
Update 更新
You're still using an illegal (closed) file handle here: FileWrapper(temp)
您仍在此处使用非法(关闭)文件句柄:
FileWrapper(temp)
Correct code would be: 正确的代码是:
wrapper = DeletingFileWrapper(open(temp.name, 'b'))
And you need to use a method which takes a file name to determine the length because open(temp.name).tell()
always returns 0. Check the os
module. 并且您需要使用一种采用文件名来确定长度的方法,因为
open(temp.name).tell()
始终返回0。检查os
模块。
See also: 也可以看看:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.