0%

Python3.4在内存中生成zip压缩文件

最近使用Django1.8.5搭建了一个Web项目,用来生成Android客户端的皮肤apk,相当于一个在线的皮肤apk生成工具,于是就理所当然的需要进行在线的下载apk的操作。由于Android项目比较大,一种主题皮肤对应的apk不止一个,一次性下载多个文件的话,于是选择打包下载。
本文在使用Python在内存中生成zip文件的基础上也进行了小的修改。主要是原文的生产环境是Python2.x,而我使用的是Python3.4,在语法上有些小的变动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# !user/bin/env python3
# -*-coding : utf-8 -*-

import zipfile
from io import BytesIO
import os

u'''
Create zip file in memory.
'''
class InMemoryZIP(object):

def __init__(self):
# create the in-memory file-like object
self.in_memory_zip = BytesIO()

def append(self, filename_in_zip, file_contents):
""" Appends a file with name filename_in_zip \
and contents of file_contents to the in-memory zip.
"""
# create a handle to the in-memory zip in append mode
zf = zipfile.ZipFile(self.in_memory_zip, 'a',
zipfile.ZIP_DEFLATED, False)

# write the file to the in-memory zip
zf.writestr(filename_in_zip, file_contents)

# mark the files as having been created on Windows
# so that Unix permissions are not inferred as 0000
for zfile in zf.filelist:
zfile.create_system = 0
return self

def appendfile(self, file_path, file_name=None):
""" Read a file with path file_path \
and append to in-memory zip with name file_name.
"""
if file_name is None:
file_name = os.path.split(file_path)[1]

f = open(file_path, 'rb')
file_contents = f.read()
self.append(file_name, file_contents)
f.close()
return self

def read(self):
""" Returns a string with the contents of the in-memory zip.
"""
self.in_memory_zip.seek(0)
return self.in_memory_zip.read()

def writetofile(self, filename):
"""
Write the in-memory zip to a file
"""
f = open(filename, 'wb')
f.write(self.read())
f.close()

if __name__ == '__main__':
imz = InMemoryZIP()
imz.appendfile('a.txt').append('test.txt', 'This is content in test.txt')
imz.writetofile('test.zip')

与原链接中的博文相比主要改动如下:
  将原文的 import StringIO 改成了 from io import BytesIO,主要就是 Python2.x 和 Python3.x 的格式区别。其实也可以将 import StringIO 改成 from io import BytesIO as StringIO ,这样的话在代码中就不需要进行替换,但是可能会误导其他读这段代码的人。

在Django中简单的使用方法为:

1
2
3
4
5
6
7
8
9
10
11
12
13
#...
def downloadFiles(request):
fns = ["/apk/foo1.apk", "/apk/foo2.apk", "/apk/foo3.apk",]
imz = InMemoryZip()
for fn in fns:
imz.appendfile(fn)
data = img.read()

response = HttpResponse(content_type="application/octet-stream")
response["Content-Disposition"] = "attachment; %s" % "foo.zip"
response["Content-Length"] = len(data)
response.write(data)
return response

与原链接的博文相比改动就是 HttpResponse 中的参数要使用 content_type 而不是 mimetype
最后引用原文的一段话:

这个方法虽然很方便,不过很耗资源,我试着用它在 Django 里压缩一个 1.4G 的文本文件,差不多用了 8 分钟,期间 CPU 使用率一直是 100%,所以,如果要压缩的是大文件,或者压缩任务比较频繁,可能需要认真处理一下性能问题。

也就是说这个方法适合下载小文件,要是下载的文件较大的话,建议阅读以下文章: