有没有办法用Python从zip文件中提供用户上传的图片?
我正在制作一个网站,用户可以上传图片画廊(通常每个画廊有几十张图片),而且用户在上传之前需要把这些图片压缩成一个zip文件。我注意到(至少在Linux系统上),我可以直接点击一个压缩文件,查看里面的图片,而不用先解压缩。请问,使用Python能否像这样直接从zip文件中提供图片,因为解压缩会消耗很多CPU资源?
1 个回答
3
你可以使用Python的zipfile模块来读取zip文件的内容,这样可以实时解压和提供文件。不过,这样做是否真的合适就要另说了,因为正如你提到的,解压文件会比直接从文件系统提供文件消耗更多的CPU资源。
我猜用户上传zip文件是为了能一次性上传多张图片。在这种情况下,最好是在文件上传后再解压zip文件。你可以将解压后的文件放到一个可以通过网页服务器提供静态文件的位置。
示例应用
下面的代码展示了一个示例Flask应用,演示了如何直接从zip文件中提供图片。这个示例省略了很多细节,所以不要直接在生产环境中使用!
要测试这个应用,先安装flask(最好在虚拟环境中),然后运行代码:
virtualenv env
env/bin/pip install flask
env/bin/python sample_flask_application.py
确保在Flask应用的同级目录下有一个名为galleries
的文件夹,并且在galleries
文件夹里有几个包含jpeg图片的.zip
文件。
import os
import zipfile
from flask import Flask
from flask import Response
app = Flask(__name__)
gallery_path = 'galleries'
@app.route('/')
def index():
html = '<a href="{0}">{0}</a>'
gallery_zips = os.listdir(gallery_path)
gallery_names = [os.path.splitext(zfile)[0] for zfile in gallery_zips]
galleries = [html.format(gallery) for gallery in gallery_names]
return '<br>'.join(galleries)
@app.route('/<gallery>')
def gallery(gallery):
zip_path = os.path.join(gallery_path, gallery + '.zip')
html = '<a href="{0}/{1}">{1}</a>'
with zipfile.ZipFile(zip_path, 'r') as zfp:
images = zfp.namelist()
image_list = [html.format(gallery, image) for image in images]
return '<br>'.join(image_list)
@app.route('/<gallery>/<path:image>')
def image(gallery, image):
zip_path = os.path.join(gallery_path, gallery + '.zip')
with zipfile.ZipFile(zip_path) as zfp:
image_fp = zfp.open(image, 'r')
return Response(image_fp, mimetype='image/jpeg')
if __name__ == '__main__':
app.run(debug=True)
可能的改进
- 在
image
函数中设置过期时间和其他可能帮助缓存图片文件的设置 - 使用解压后的图片缓存
- 在提供图片时,先检查缓存位置是否有这张图片
- 如果没有,就解压到缓存位置
- 然后用send_file将文件提供给客户端
- 缓存每个zip文件中包含的文件列表
- 如果图片实际上不在zip文件中,生成404错误!
总结
上面的代码是可以工作的,特别是当你的网站流量不高的时候。如果每天只有几个人查看几张图片,那就没问题。如果你稍微优化一下代码(设置过期时间、缓存),那么可以处理更多的流量。不过如果你想要最佳性能,那就需要在上传时解压zip文件,并直接通过网页服务器提供文件(比如nginx、apache或你正在使用的其他服务器)。当你有成千上万的访客来自世界各地时,你还可以考虑使用内容分发网络。