Rumah > Artikel > pangkalan data > Flask / MongoDB 搭建简易图片服务器
前期准备 通过 pip 或 easy_install 安装了 pymongo 之后, 就能通过 Python 调教 mongodb 了. 接着安装个 flask 用来当 web 服务器. 当然 mongo 也是得安装的. 对于 Ubuntu 用户, 特别是使用 Server 12.04 的同学, 安装最新版要略费些周折, 具体说是 sudoapt
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10<br>echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list<br>sudo apt-get update<br>sudo apt-get install mongodb-10gen<br>
pip install Pillow<br>
easy_install Pillow<br>
import flask<br><br>app = flask.Flask(__name__)<br>app.debug = True<br><br>@app.route('/upload', methods=['POST'])<br>def upload():<br> f = flask.request.files['uploaded_file']<br> print f.read()<br> return flask.redirect('/')<br><br>@app.route('/')<br>def index():<br> return '''<br> nbsp;html><br> <br> <br> <form>
<br> <input><br> <input><br> </form>
<br> '''<br><br>if __name__ == '__main__':<br> app.run(port=7777)<br>
upload
函数中, 使用 flask.request.files[KEY]
获取上传文件对象, KEY 为页面 form 中 input 的 name 值import pymongo<br>import bson.binary<br>from cStringIO import StringIO<br><br>app = flask.Flask(__name__)<br>app.debug = True<br><strong class="ntstrong">db = pymongo.MongoClient('localhost', 27017).test</strong><br><br>def save_file(f):<br> content = StringIO(f.read())<br> db.files.save(dict(<br> content=<strong class="ntstrong">bson.binary.Binary(content.getvalue())</strong>,<br> ))<br><br>@app.route('/upload', methods=['POST'])<br>def upload():<br> f = flask.request.files['uploaded_file']<br> <strong class="ntstrong">save_file(f)</strong><br> return flask.redirect('/')<br>
bson.binary.Binary
对象, 再把它扔进 mongodb 就可以了.db.files.find()<br>
content
这个域几乎肉眼无法分辨出什么东西, 即使是纯文本文件, mongo 也会显示为 Base64 编码.def save_file(f):<br> content = StringIO(f.read())<br> <strong class="ntstrong">c = dict(content=bson.binary.Binary(content.getvalue()))</strong><br> <strong class="ntstrong">db.files.save(c)</strong><br> <strong class="ntstrong">return c['_id']</strong><br><br>@app.route('/f/<fid>')<br><strong class="ntstrong">def serve_file(fid):</strong><br> f = db.files.find_one(bson.objectid.ObjectId(fid))<br> return f['content']<br><br>@app.route('/upload', methods=['POST'])<br>def upload():<br> f = flask.request.files['uploaded_file']<br> fid = save_file(f)<br> return flask.redirect(<strong class="ntstrong">'/f/' + str(fid)</strong>)<br></fid>
upload
函数会跳转到对应的文件浏览页. 这样一来, 文本文件内容就可以正常预览了, 如果不是那么挑剔换行符跟连续空格都被浏览器吃掉的话.bson.errors.InvalidId
; 其二, 找不到对象 (!), 这时 pymongo 会返回 None
.@app.route('/f/<fid>')<br>def serve_file(fid):<br> import bson.errors<br> try:<br> f = db.files.find_one(bson.objectid.ObjectId(fid))<br> if f is None:<br> raise bson.errors.InvalidId()<br> return f['content']<br> except bson.errors.InvalidId:<br> flask.abort(404)<br></fid>
from PIL import Image<br><br>allow_formats = set(['jpeg', 'png', 'gif'])<br><br>def save_file(f):<br> content = StringIO(f.read())<br> try:<br> mime = <strong class="ntstrong">Image.open(content).format.lower()</strong><br> if mime not in allow_formats:<br> raise IOError()<br> except IOError:<br> flask.abort(400)<br> c = dict(content=bson.binary.Binary(content.getvalue()))<br> db.files.save(c)<br> return c['_id']<br>
def save_file(f):<br> content = StringIO(f.read())<br> try:<br> mime = Image.open(content).format.lower()<br> if mime not in allow_formats:<br> raise IOError()<br> except IOError:<br> flask.abort(400)<br> c = dict(content=bson.binary.Binary(content.getvalue()), mime=mime)<br> db.files.save(c)<br> return c['_id']<br><br>@app.route('/f/<fid>')<br>def serve_file(fid):<br> try:<br> f = db.files.find_one(bson.objectid.ObjectId(fid))<br> if f is None:<br> raise bson.errors.InvalidId()<br> return <strong class="ntstrong">flask.Response(f['content'], mimetype='image/' + f['mime'])</strong><br> except bson.errors.InvalidId:<br> flask.abort(404)<br></fid>
db.files.drop()
清掉原来的数据.import datetime<br><br>def save_file(f):<br> content = StringIO(f.read())<br> try:<br> mime = Image.open(content).format.lower()<br> if mime not in allow_formats:<br> raise IOError()<br> except IOError:<br> flask.abort(400)<br> c = dict(<br> content=bson.binary.Binary(content.getvalue()),<br> mime=mime,<br> <strong class="ntstrong">time=datetime.datetime.utcnow()</strong>,<br> )<br> db.files.save(c)<br> return c['_id']<br><br>@app.route('/f/<fid>')<br>def serve_file(fid):<br> try:<br> f = db.files.find_one(bson.objectid.ObjectId(fid))<br> if f is None:<br> raise bson.errors.InvalidId()<br> if <strong class="ntstrong">flask.request.headers.get('If-Modified-Since') == f['time'].ctime()</strong>:<br> return <strong class="ntstrong">flask.Response(status=304)</strong><br> resp = flask.Response(f['content'], mimetype='image/' + f['mime'])<br> <strong class="ntstrong">resp.headers['Last-Modified'] = f['time'].ctime()</strong><br> return resp<br> except bson.errors.InvalidId:<br> flask.abort(404)<br></fid>
db.files.ensureIndex({sha1: 1}, {unique: true})<br>
import hashlib<br><br>def save_file(f):<br> content = StringIO(f.read())<br> try:<br> mime = Image.open(content).format.lower()<br> if mime not in allow_formats:<br> raise IOError()<br> except IOError:<br> flask.abort(400)<br><br> <strong class="ntstrong">sha1 = hashlib.sha1(content.getvalue()).hexdigest()</strong><br> c = dict(<br> content=bson.binary.Binary(content.getvalue()),<br> mime=mime,<br> time=datetime.datetime.utcnow(),<br> <strong class="ntstrong">sha1=sha1</strong>,<br> )<br> <strong class="ntstrong">try:</strong><br> db.files.save(c)<br> <strong class="ntstrong">except pymongo.errors.DuplicateKeyError:</strong><br> pass<br> return c['_id']<br>
c['_id']
将会是一个不存在的数据 ID. 修正这个问题, 最好是返回 sha1
, 另外, 在访问文件时, 相应地修改为用文件 SHA-1 访问, 而不是用 ID.import hashlib<br>import datetime<br>import flask<br>import pymongo<br>import bson.binary<br>import bson.objectid<br>import bson.errors<br>from cStringIO import StringIO<br>from PIL import Image<br><br>app = flask.Flask(__name__)<br>app.debug = True<br>db = pymongo.MongoClient('localhost', 27017).test<br>allow_formats = set(['jpeg', 'png', 'gif'])<br><br>def save_file(f):<br> content = StringIO(f.read())<br> try:<br> mime = Image.open(content).format.lower()<br> if mime not in allow_formats:<br> raise IOError()<br> except IOError:<br> flask.abort(400)<br><br> sha1 = hashlib.sha1(content.getvalue()).hexdigest()<br> c = dict(<br> content=bson.binary.Binary(content.getvalue()),<br> mime=mime,<br> time=datetime.datetime.utcnow(),<br> sha1=sha1,<br> )<br> try:<br> db.files.save(c)<br> except pymongo.errors.DuplicateKeyError:<br> pass<br> return sha1<br><br>@app.route('/f/<sha1>')<br>def serve_file(sha1):<br> try:<br> f = db.files.find_one({'sha1': sha1})<br> if f is None:<br> raise bson.errors.InvalidId()<br> if flask.request.headers.get('If-Modified-Since') == f['time'].ctime():<br> return flask.Response(status=304)<br> resp = flask.Response(f['content'], mimetype='image/' + f['mime'])<br> resp.headers['Last-Modified'] = f['time'].ctime()<br> return resp<br> except bson.errors.InvalidId:<br> flask.abort(404)<br><br>@app.route('/upload', methods=['POST'])<br>def upload():<br> f = flask.request.files['uploaded_file']<br> sha1 = save_file(f)<br> return flask.redirect('/f/' + str(sha1))<br><br>@app.route('/')<br>def index():<br> return '''<br> nbsp;html><br> <br> <br> <form>
<br> <input><br> <input><br> </form>
<br> '''<br><br>if __name__ == '__main__':<br> app.run(port=7777)<br></sha1>
原文地址:Flask / MongoDB 搭建简易图片服务器, 感谢原作者分享。