Python中使用Flask、MongoDB搭建简易图片服务器
转自:http://www.ctolib.com/topics-43840.html
1、前期准备
通过 pip 或 easy_install 安装了 pymongo 之后, 就能通过 Python 调教 mongodb 了.
接着安装个 flask 用来当 web 服务器.
当然 mongo 也是得安装的. 对于 Ubuntu 用户, 特别是使用 Server 12.04 的同学, 安装最新版要略费些周折, 具体说是
1 2 3 4 | sudo apt - key adv - - keyserver hkp: / / keyserver.ubuntu.com: 80 - - recv 7F0CEB10 echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee / etc / apt / sources. list .d / mongodb. list sudo apt - get update sudo apt - get install mongodb - 10gen |
如果你跟我一样觉得让通过上传文件名的后缀判别用户上传的什么文件完全是捏着山药当小黄瓜一样欺骗自己, 那么最好还准备个 Pillow 库
pip install Pillow
2、正片
2.1 Flask 文件上传
Flask 官网上那个例子居然分了两截让人无从吐槽. 这里先弄个最简单的, 无论什么文件都先弄上来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import flask app = flask.Flask(__name__) app.debug = True @app .route( '/upload' , methods = [ 'POST' ]) def upload(): f = flask.request.files[ 'uploaded_file' ] print f.read() return flask.redirect( '/' ) @app .route( '/' ) def index(): return ''' <!doctype html> <html> <body> <form action='/upload' method='post' enctype='multipart/form-data'> <input type='file' name='uploaded_file'> <input type='submit' value='Upload'> </form> ''' if __name__ = = '__main__' : app.run(port = 7777 ) |
注: 在 upload 函数中, 使用 flask.request.files[KEY] 获取上传文件对象, KEY 为页面 form 中 input 的 name 值
因为是在后台输出内容, 所以测试最好拿纯文本文件来测.
2.2 保存到 mongodb
如果不那么讲究的话, 最快速基本的存储方案里只需要
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import pymongo import bson.binary from cStringIO import StringIO app = flask.Flask(__name__) app.debug = True db = pymongo.MongoClient( 'localhost' , 27017 ).test def save_file(f): content = StringIO(f.read()) db.files.save( dict ( content = bson.binary.Binary(content.getvalue()), )) @app .route( '/upload' , methods = [ 'POST' ]) def upload(): f = flask.request.files[ 'uploaded_file' ] save_file(f) return flask.redirect( '/' ) |
把内容塞进一个 bson.binary.Binary 对象, 再把它扔进 mongodb 就可以了.
现在试试再上传个什么文件, 在 mongo shell 中通过 db.files.find() 就能看到了.
不过 content 这个域几乎肉眼无法分辨出什么东西, 即使是纯文本文件, mongo 也会显示为 Base64 编码.
2.3 提供文件访问
给定存进数据库的文件的 ID (作为 URI 的一部分), 返回给浏览器其文件内容, 如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def save_file(f): content = StringIO(f.read()) c = dict (content = bson.binary.Binary(content.getvalue())) db.files.save(c) return c[ '_id' ] @app .route( '/f/<fid>' ) def serve_file(fid): f = db.files.find_one(bson.objectid.ObjectId(fid)) return f[ 'content' ] @app .route( '/upload' , methods = [ 'POST' ]) def upload(): f = flask.request.files[ 'uploaded_file' ] fid = save_file(f) return flask.redirect( '/f/' + str (fid)) |
上传文件之后, upload 函数会跳转到对应的文件浏览页. 这样一来, 文本文件内容就可以正常预览了, 如果不是那么挑剔换行符跟连续空格都被浏览器吃掉的话.
2.4 当找不到文件时
有两种情况, 其一, 数据库 ID 格式就不对, 这时 pymongo 会抛异常 bson.errors.InvalidId ; 其二, 找不到对象 (!), 这时 pymongo 会返回 None .
简单起见就这样处理了
1 2 3 4 5 6 7 8 9 10 | @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
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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() 清掉原来的数据.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」