Python中使用Flask、MongoDB搭建简易图片服务器

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介:

转自: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
5
sudo apt - key adv - - keyserver <span class = "hljs-string" >hkp:<span class = "hljs-comment" > / / keyserver.ubuntu.com: 80  - - recv 7F0CEB10
echo <span class = "hljs-string" > 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen'  | sudo tee <span class = "hljs-regexp" > / etc / apt<span class = "hljs-regexp" > / sources. list .d / mongodb. list
sudo apt - get update
sudo apt - get install mongodb<span class = "hljs-number" > - 10gen
< / span>< / span>< / span>< / span>< / span>< / span>

如果你跟我一样觉得让通过上传文件名的后缀判别用户上传的什么文件完全是捏着山药当小黄瓜一样欺骗自己, 那么最好还准备个 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
22
<span class = "hljs-keyword" > import  flask
app =  flask.Flask(__name__)
app.debug =  <span class = "hljs-keyword" > True
<span class = "hljs-meta" >@app.route( '/upload' , methods = [ 'POST' ])
<span class = "hljs-function" ><span class = "hljs-keyword" > def  <span class = "hljs-title" >upload<span class = "hljs-params" >():
   f =  flask.request.files[<span class = "hljs-string" > 'uploaded_file' ]
   <span class = "hljs-keyword" > print  f.read()
   <span class = "hljs-keyword" > return  flask.redirect(<span class = "hljs-string" > '/' )
<span class = "hljs-meta" >@app.route( '/' )
<span class = "hljs-function" ><span class = "hljs-keyword" > def  <span class = "hljs-title" >index<span class = "hljs-params" >():
   <span class = "hljs-keyword" > return  <span class = "hljs-string" > '''
   <!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>
   '''
<span class = "hljs-keyword" > if  __name__ = =  <span class = "hljs-string" > '__main__' :
   app.run(port = <span class = "hljs-number" > 7777 )
< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>

注: 在 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
17
18
<span class = "hljs-keyword" > import  pymongo
<span class = "hljs-keyword" > import  bson.binary
<span class = "hljs-keyword" > from  cStringIO <span class = "hljs-keyword" > import  StringIO
app =  flask.Flask(__name__)
app.debug =  <span class = "hljs-keyword" > True
db =  pymongo.MongoClient(<span class = "hljs-string" > 'localhost' , <span class = "hljs-number" > 27017 ).test
<span class = "hljs-function" ><span class = "hljs-keyword" ><span style = "color: #ff0000" > def  < / span><span class = "hljs-title" ><span style = "color: #ff0000" >save_file< / span><span class = "hljs-params" ><span style = "color: #ff0000" >(f):
   content =  StringIO(f.read())
   db.files.save( dict (
     content =  bson.binary.Binary(content.getvalue()),
   ))< / span>
<span class = "hljs-meta" >@app.route( '/upload' , methods = [ 'POST' ])
<span class = "hljs-function" ><span class = "hljs-keyword" > def  <span class = "hljs-title" >upload<span class = "hljs-params" >():
   f =  flask.request.files[<span class = "hljs-string" > 'uploaded_file' ]
   save_file(f)
   <span class = "hljs-keyword" > return  flask.redirect(<span class = "hljs-string" > '/' )
 
< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>

把内容塞进一个  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
15
16
<span class = "hljs-function" ><span class = "hljs-keyword" > def  <span class = "hljs-title" >save_file<span class = "hljs-params" >(f):
    content =  StringIO(f.read())
    c =  dict (content = bson.binary.Binary(content.getvalue()))
    db.files.save(c)
    <span class = "hljs-keyword" > return  c[<span class = "hljs-string" > '_id' ]
<span class = "hljs-meta" >@app.route( '/f/<fid>' )
<span class = "hljs-function" ><span class = "hljs-keyword" > def  <span class = "hljs-title" >serve_file<span class = "hljs-params" >(fid):
   f =  db.files.find_one(bson.objectid.ObjectId(fid))
   <span class = "hljs-keyword" > return  f[<span class = "hljs-string" > 'content' ]
<span class = "hljs-meta" >@app.route( '/upload' , methods = [ 'POST' ])
<span class = "hljs-function" ><span class = "hljs-keyword" > def  <span class = "hljs-title" >upload<span class = "hljs-params" >():
   f =  flask.request.files[<span class = "hljs-string" > 'uploaded_file' ]
   fid =  save_file(f)
   <span class = "hljs-keyword" > return  flask.redirect( <span class = "hljs-string" > '/f/'  +  str (fid))
 
< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>

上传文件之后,  upload  函数会跳转到对应的文件浏览页. 这样一来, 文本文件内容就可以正常预览了, 如果不是那么挑剔换行符跟连续空格都被浏览器吃掉的话.

2.4 当找不到文件时

有两种情况, 其一, 数据库 ID 格式就不对, 这时 pymongo 会抛异常  bson.errors.InvalidId ; 其二, 找不到对象 (!), 这时 pymongo 会返回  None .
简单起见就这样处理了

1
2
3
4
5
6
7
8
9
10
11
12
<span class = "hljs-meta" >@app.route( '/f/<fid>' )
<span class = "hljs-function" ><span class = "hljs-keyword" > def  <span class = "hljs-title" >serve_file<span class = "hljs-params" >(fid):
   <span class = "hljs-keyword" > import  bson.errors
   <span class = "hljs-keyword" > try :
     f =  db.files.find_one(bson.objectid.ObjectId(fid))
     <span class = "hljs-keyword" > if  f <span class = "hljs-keyword" > is  <span class = "hljs-keyword" > None :
       <span class = "hljs-keyword" > raise  bson.errors.InvalidId()
     <span class = "hljs-keyword" > return  f[<span class = "hljs-string" > 'content' ]
   <span class = "hljs-keyword" > except  bson.errors.InvalidId:
     flask.abort(<span class = "hljs-number" > 404 )
 
< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>

2.5 正确的 MIME

从现在开始要对上传的文件严格把关了, 文本文件, 狗与剪刀等皆不能上传.
判断图片文件之前说了我们动真格用 Pillow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<span class = "hljs-keyword" > from  PIL <span class = "hljs-keyword" > import  Image
allow_formats =  set ([<span class = "hljs-string" > 'jpeg' , <span class = "hljs-string" > 'png' , <span class = "hljs-string" > 'gif' ])
<span class = "hljs-function" ><span class = "hljs-keyword" > def  <span class = "hljs-title" >save_file<span class = "hljs-params" >(f):
   content =  StringIO(f.read())
   <span class = "hljs-keyword" > try :
     mime =  Image. open (content). format .lower()
     <span class = "hljs-keyword" > if  mime <span class = "hljs-keyword" > not  <span class = "hljs-keyword" > in  allow_formats:
       <span class = "hljs-keyword" > raise  IOError()
   <span class = "hljs-keyword" > except  IOError:
     flask.abort(<span class = "hljs-number" > 400 )
   c =  dict (content = bson.binary.Binary(content.getvalue()))
   db.files.save(c)
   <span class = "hljs-keyword" > return  c[<span class = "hljs-string" > '_id' ]
 
< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>

然后试试上传文本文件肯定虚, 传图片文件才能正常进行. 不对, 也不正常, 因为传完跳转之后, 服务器并没有给出正确的 mimetype, 所以仍然以预览文本的方式预览了一坨二进制乱码.
要解决这个问题, 得把 MIME 一并存到数据库里面去; 并且, 在给出文件时也正确地传输 mimetype

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<span class = "hljs-function" ><span class = "hljs-keyword" > def  <span class = "hljs-title" >save_file<span class = "hljs-params" >(f):
   content =  StringIO(f.read())
   <span class = "hljs-keyword" > try :
     mime =  Image. open (content). format .lower()
     <span class = "hljs-keyword" > if  mime <span class = "hljs-keyword" > not  <span class = "hljs-keyword" > in  allow_formats:
       <span class = "hljs-keyword" > raise  IOError()
   <span class = "hljs-keyword" > except  IOError:
     flask.abort(<span class = "hljs-number" > 400 )
   c =  dict (content = bson.binary.Binary(content.getvalue()), mime = mime)
   db.files.save(c)
   <span class = "hljs-keyword" > return  c[<span class = "hljs-string" > '_id' ]
<span class = "hljs-meta" >@app.route( '/f/<fid>' )
<span class = "hljs-function" ><span class = "hljs-keyword" > def  <span class = "hljs-title" >serve_file<span class = "hljs-params" >(fid):
   <span class = "hljs-keyword" > try :
     f =  db.files.find_one(bson.objectid.ObjectId(fid))
     <span class = "hljs-keyword" > if  f <span class = "hljs-keyword" > is  <span class = "hljs-keyword" > None :
       <span class = "hljs-keyword" > raise  bson.errors.InvalidId()
     <span class = "hljs-keyword" > return  flask.Response(f[<span class = "hljs-string" > 'content' ], mimetype = <span class = "hljs-string" > 'image/'  +  f[<span class = "hljs-string" > 'mime' ])
   <span class = "hljs-keyword" > except  bson.errors.InvalidId:
     flask.abort(<span class = "hljs-number" > 404 )
 
< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>< / span>

当然这样的话原来存进去的东西可没有 mime 这个属性, 所以最好先去 mongo shell 用  db.files.drop()  清掉原来的数据.














本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/bonelee/p/6513455.html,如需转载请自行联系原作者

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
6天前
|
移动开发 JavaScript 前端开发
精通服务器推送事件(SSE)与 Python 和 Go 实现实时数据流 🚀
服务器推送事件(SSE)是HTML5规范的一部分,允许服务器通过HTTP向客户端实时推送更新。相比WebSocket,SSE更轻量、简单,适合单向通信场景,如实时股票更新或聊天消息。它基于HTTP协议,使用`EventSource` API实现客户端监听,支持自动重连和事件追踪。虽然存在单向通信与连接数限制,但其高效性使其成为许多轻量级实时应用的理想选择。文中提供了Python和Go语言的服务器实现示例,以及HTML/JavaScript的客户端代码,帮助开发者快速集成SSE功能,提升用户体验。
|
5月前
|
Python
Python实用记录(六):如何打开txt文档并删除指定绝对路径下图片
这篇文章介绍了如何使用Python打开txt文档,删除文档中指定路径的图片,并提供了一段示例代码来展示这一过程。
62 1
|
14天前
|
Docker Python 容器
Docker——阿里云服务器使用Docker部署python项目全程小记
本文记录了我在阿里云服务器上使用Docker部署python项目(flask为例)的全过程,在这里记录和分享一下,希望可以给大家提供一些参考。
|
3月前
|
弹性计算 安全 开发工具
灵码评测-阿里云提供的ECS python3 sdk做安全组管理
批量变更阿里云ECS安全组策略(批量变更)
|
4月前
|
缓存 监控 Linux
Python 实时获取Linux服务器信息
Python 实时获取Linux服务器信息
|
4月前
|
NoSQL 容灾 MongoDB
MongoDB主备副本集方案:两台服务器使用非对称部署的方式实现高可用与容灾备份
在资源受限的情况下,为了实现MongoDB的高可用性,本文探讨了两种在两台服务器上部署MongoDB的方案。方案一是通过主备身份轮换,即一台服务器作为主节点,另一台同时部署备节点和仲裁节点;方案二是利用`priority`设置实现自动主备切换。两者相比,方案二自动化程度更高,适合追求快速故障恢复的场景,而方案一则提供了更多的手动控制选项。文章最后对比了这两种方案与标准三节点副本集的优缺点,指出三节点方案在高可用性和数据一致性方面表现更佳。
354 5
|
5月前
|
Python
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
使用Python的socket库实现客户端到服务器端的图片传输,包括客户端和服务器端的代码实现,以及传输结果的展示。
242 3
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
|
5月前
|
IDE 网络安全 开发工具
IDE之pycharm:专业版本连接远程服务器代码,并配置远程python环境解释器(亲测OK)。
本文介绍了如何在PyCharm专业版中连接远程服务器并配置远程Python环境解释器,以便在服务器上运行代码。
968 0
IDE之pycharm:专业版本连接远程服务器代码,并配置远程python环境解释器(亲测OK)。
|
5月前
|
Python
Python实用记录(四):os模块-去后缀或者改后缀/指定目录下图片或者子目录图片写入txt/csv
本文介绍了如何使用Python的os模块来操作文件,包括更改文件后缀、分割文件路径和后缀、将指定目录下的所有图片写入txt文档,以及将指定目录下所有子目录中的图片写入csv文档,并为每个子目录分配一个标签。
66 1
|
6天前
|
存储 NoSQL MongoDB
微服务——MongoDB常用命令1——数据库操作
本节介绍了 MongoDB 中数据库的选择、创建与删除操作。使用 `use 数据库名称` 可选择或创建数据库,若数据库不存在则自动创建。通过 `show dbs` 或 `show databases` 查看所有可访问的数据库,用 `db` 命令查看当前数据库。注意,集合仅在插入数据后才会真正创建。数据库命名需遵循 UTF-8 格式,避免特殊字符,长度不超过 64 字节,且部分名称如 `admin`、`local` 和 `config` 为系统保留。删除数据库可通过 `db.dropDatabase()` 实现,主要用于移除已持久化的数据库。
32 0

热门文章

最新文章