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

时间:2019-03-27 13:11:24

标签: python django

我在项目根文件夹中有一些图片,我想让它们可作为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

1 个答案:

答案 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模块。

另请参阅: