上传文件之后, upload 函数会跳转到对应的文件浏览页. 这样一来, 文本文件内容就可以正常预览了, 如果不是那么挑剔换行符跟连续空格都被浏览器吃掉的话.
2.4 当找不到文件时
有两种情况, 其一, 数据库 ID 格式就不对, 这时 pymongo 会抛异常 bson.errors.InvalidId ; 其二, 找不到对象 (!), 这时 pymongo 会返回 None .
简单起见就这样处理了
@app.route('/f/<fid>')
def serve_file(fid):
import bson.errors
try:
f = db.files.find_one(bson.objectid.ObjectId(fid))
if f is None:
raise bson.errors.InvalidId()
return f['content']
except bson.errors.InvalidId:
flask.abort(404)
2.5 正确的 MIME
从现在开始要对上传的文件严格把关了, 文本文件, 狗与剪刀等皆不能上传.
判断图片文件之前说了我们动真格用 Pillow
from PIL import Image
allow_formats = set(['jpeg', 'png', 'gif'])
def save_file(f):
content = StringIO(f.read())
try:
mime = Image.open(content).format.lower()
if mime not in allow_formats:
raise IOError()
except IOError:
flask.abort(400)
c = dict(content=bson.binary.Binary(content.getvalue()))
db.files.save(c)
return c['_id']
然后试试上传文本文件肯定虚, 传图片文件才能正常进行. 不对, 也不正常, 因为传完跳转之后, 服务器并没有给出正确的 mimetype, 所以仍然以预览文本的方式预览了一坨二进制乱码.
要解决这个问题, 得把 MIME 一并存到数据库里面去; 并且, 在给出文件时也正确地传输 mimetype
def save_file(f):
content = StringIO(f.read())
try:
mime = Image.open(content).format.lower()
if mime not in allow_formats:
raise IOError()
except IOError:
flask.abort(400)
c = dict(content=bson.binary.Binary(content.getvalue()), mime=mime)
db.files.save(c)
return c['_id']
@app.route('/f/<fid>')
def serve_file(fid):
try:
f = db.files.find_one(bson.objectid.ObjectId(fid))
if f is None:
raise bson.errors.InvalidId()
return flask.Response(f['content'], mimetype='image/' + f['mime'])
except bson.errors.InvalidId:
flask.abort(404)
当然这样的话原来存进去的东西可没有 mime 这个属性, 所以最好先去 mongo shell 用 db.files.drop() 清掉原来的数据.
2.6 根据上传时间给出 NOT MODIFIED
利用 HTTP 304 NOT MODIFIED 可以尽可能压榨与利用浏览器缓存和节省带宽. 这需要三个操作
1)、记录文件最后上传的时间
2)、当浏览器请求这个文件时, 向请求头里塞一个时间戳字符串
3)、当浏览器请求文件时, 从请求头中尝试获取这个时间戳, 如果与文件的时间戳一致, 就直接 304
体现为代码是
import datetime
def save_file(f):
content = StringIO(f.read())
try:
mime = Image.open(content).format.lower()
if mime not in allow_formats:
raise IOError()
except IOError:
flask.abort(400)
c = dict(
content=bson.binary.Binary(content.getvalue()),
mime=mime,
time=datetime.datetime.utcnow(),
)
db.files.save(c)
return c['_id']
@app.route('/f/<fid>')
def serve_file(fid):
try:
f = db.files.find_one(bson.objectid.ObjectId(fid))
if f is None:
raise bson.errors.InvalidId()
if flask.request.headers.get('If-Modified-Since') == f['time'].ctime():
return flask.Response(status=304)
resp = flask.Response(f['content'], mimetype='image/' + f['mime'])
resp.headers['Last-Modified'] = f['time'].ctime()
return resp
except bson.errors.InvalidId:
flask.abort(404)










