简体   繁体   English

将某些文件下载为一个压缩存档

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM