Python Flask:拖拽上传和进度条
来自CloudWiki
先备知识
现在,一切都还很简陋。为了让上传功能更加美观和易用,需要增加一些辅助的功能,比如上传进度条,拖拽上传,显示缩略图预览,显示文件信息。如果自己实现这些需要很多时间和精力,我们可以借助一些插件和库实现。
知识点
Dropzone.js
Dropzone.js比较轻量,自定义选项很丰富,而且不依赖其他外部库。加载相应文件后,你只需要创建一个类的值为dropzone的表单就可以了。一个简单的使用Flask和Dropzone上传文件的例子在这里:
helloflask - flask-upload-dropzone: https://github.com/helloflask/flask-upload-dropzone
另外,为了方便集成Dropzone,我写了一个扩展:Flask-Dropzone:https://greyli.com/flask-dropzone/
jQuery File Upload
https://blueimp.github.io/jQuery-File-Upload/
jQuery File Upload是比较流行的上传插件,基本实现了所有常用的功能。借助我们之前学习的内容,可以基于它实现一个Flask版本。
这里有一个Flask版本:greyli/flask-file-uploader
https://github.com/greyli/flask-file-uploader
这里使用Pillow来处理图像生成缩略图:
import PIL from PIL import Image def create_thumbnail(image): base_width = 80 img = Image.open(os.path.join(app.config['UPLOAD_FOLDER'], image)) w_percent = (base_width / float(img.size[0])) h_size = int((float(img.size[1]) * float(w_percent))) img = img.resize((base_width, h_size), PIL.Image.ANTIALIAS) img.save(os.path.join(app.config['THUMBNAIL_FOLDER'], image))
上面的函数把图片转换成定宽80的缩略图,然后保存到相应的文件夹。
Plupload
Plupload同样支持拖拽上传,文件管理等功能。而且可以在客户端缩小图片,比如生成缩略图作为头像,有多种主题可选。
找到一个集成了Plupload的Flask扩展,不过并没有测试,而且没有文档。如果不好用可以自己尝试写一个。
相关链接
- Dropzone.js:http://www.dropzonejs.com/
- 使用Flask和Dropzone.js上传的示例程序:https://github.com/helloflask/flask-upload-dropzone
- jQuery-File-Upload源码:blueimp/jQuery-File-Upload
- Flask版本的jQuery-File-Upload:greyli/flask-file-uploader
- Plupload源码:https://github.com/moxiecode/plupload
- 集成Plupload的Flask扩展:ryanolson/flask-plupload
完整代码
app.py
#!flask/bin/python # Author: Ngo Duy Khanh # Email: ngokhanhit@gmail.com # Git repository: https://github.com/ngoduykhanh/flask-file-uploader # This work based on jQuery-File-Upload which can be found at https://github.com/blueimp/jQuery-File-Upload/ import os import PIL from PIL import Image import simplejson import traceback from flask import Flask, request, render_template, redirect, url_for, send_from_directory from flask_bootstrap import Bootstrap from werkzeug import secure_filename from lib.upload_file import uploadfile app = Flask(__name__) app.config['SECRET_KEY'] = 'hard to guess string' app.config['UPLOAD_FOLDER'] = 'data/' app.config['THUMBNAIL_FOLDER'] = 'data/thumbnail/' app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024 ALLOWED_EXTENSIONS = set(['txt', 'gif', 'png', 'jpg', 'jpeg', 'bmp', 'rar', 'zip', '7zip', 'doc', 'docx']) IGNORED_FILES = set(['.gitignore']) bootstrap = Bootstrap(app) def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS def gen_file_name(filename): """ If file was exist already, rename it and return a new name """ i = 1 while os.path.exists(os.path.join(app.config['UPLOAD_FOLDER'], filename)): name, extension = os.path.splitext(filename) filename = '%s_%s%s' % (name, str(i), extension) i += 1 return filename def create_thumbnail(image): try: base_width = 80 img = Image.open(os.path.join(app.config['UPLOAD_FOLDER'], image)) w_percent = (base_width / float(img.size[0])) h_size = int((float(img.size[1]) * float(w_percent))) img = img.resize((base_width, h_size), PIL.Image.ANTIALIAS) img.save(os.path.join(app.config['THUMBNAIL_FOLDER'], image)) return True except: print(traceback.format_exc()) return False @app.route("/upload", methods=['GET', 'POST']) def upload(): if request.method == 'POST': files = request.files['file'] if files: filename = secure_filename(files.filename) filename = gen_file_name(filename) mime_type = files.content_type if not allowed_file(files.filename): result = uploadfile(name=filename, type=mime_type, size=0, not_allowed_msg="File type not allowed") else: # save file to disk uploaded_file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) files.save(uploaded_file_path) # create thumbnail after saving if mime_type.startswith('image'): create_thumbnail(filename) # get file size after saving size = os.path.getsize(uploaded_file_path) # return json for js call back result = uploadfile(name=filename, type=mime_type, size=size) return simplejson.dumps({"files": [result.get_file()]}) if request.method == 'GET': # get all file in ./data directory files = [f for f in os.listdir(app.config['UPLOAD_FOLDER']) if os.path.isfile(os.path.join(app.config['UPLOAD_FOLDER'],f)) and f not in IGNORED_FILES ] file_display = [] for f in files: size = os.path.getsize(os.path.join(app.config['UPLOAD_FOLDER'], f)) file_saved = uploadfile(name=f, size=size) file_display.append(file_saved.get_file()) return simplejson.dumps({"files": file_display}) return redirect(url_for('index')) @app.route("/delete/<string:filename>", methods=['DELETE']) def delete(filename): file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) file_thumb_path = os.path.join(app.config['THUMBNAIL_FOLDER'], filename) if os.path.exists(file_path): try: os.remove(file_path) if os.path.exists(file_thumb_path): os.remove(file_thumb_path) return simplejson.dumps({filename: 'True'}) except: return simplejson.dumps({filename: 'False'}) # serve static files @app.route("/thumbnail/<string:filename>", methods=['GET']) def get_thumbnail(filename): return send_from_directory(app.config['THUMBNAIL_FOLDER'], filename=filename) @app.route("/data/<string:filename>", methods=['GET']) def get_file(filename): return send_from_directory(os.path.join(app.config['UPLOAD_FOLDER']), filename=filename) @app.route('/', methods=['GET', 'POST']) def index(): return render_template('index.html') if __name__ == '__main__': app.run(port=9191)