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
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,如需转载请自行联系原作者

相关文章
|
人工智能 JavaScript API
零基础构建MCP服务器:TypeScript/Python双语言实战指南
作为一名深耕技术领域多年的博主摘星,我深刻感受到了MCP(Model Context Protocol)协议在AI生态系统中的革命性意义。MCP作为Anthropic推出的开放标准,正在重新定义AI应用与外部系统的交互方式,它不仅解决了传统API集成的复杂性问题,更为开发者提供了一个统一、安全、高效的连接框架。在过去几个月的实践中,我发现许多开发者对MCP的概念理解透彻,但在实际动手构建MCP服务器时却遇到了各种技术壁垒。从环境配置的细节问题到SDK API的深度理解,从第一个Hello World程序的调试到生产环境的部署优化,每一个环节都可能成为初学者的绊脚石。因此,我决定撰写这篇全面的实
2927 67
零基础构建MCP服务器:TypeScript/Python双语言实战指南
|
人工智能 自然语言处理 安全
Python构建MCP服务器:从工具封装到AI集成的全流程实践
MCP协议为AI提供标准化工具调用接口,助力模型高效操作现实世界。
1962 1
|
Python
使用Python实现multipart/form-data文件接收的http服务器
至此,使用Python实现一个可以接收 'multipart/form-data' 文件的HTTP服务器的步骤就讲解完毕了。希望通过我的讲解,你可以更好地理解其中的逻辑,另外,你也可以尝试在实际项目中运用这方面的知识。
787 69
|
人工智能 安全 Shell
Jupyter MCP服务器部署实战:AI模型与Python环境无缝集成教程
Jupyter MCP服务器基于模型上下文协议(MCP),实现大型语言模型与Jupyter环境的无缝集成。它通过标准化接口,让AI模型安全访问和操作Jupyter核心组件,如内核、文件系统和终端。本文深入解析其技术架构、功能特性及部署方法。MCP服务器解决了传统AI模型缺乏实时上下文感知的问题,支持代码执行、变量状态获取、文件管理等功能,提升编程效率。同时,严格的权限控制确保了安全性。作为智能化交互工具,Jupyter MCP为动态计算环境与AI模型之间搭建了高效桥梁。
983 2
Jupyter MCP服务器部署实战:AI模型与Python环境无缝集成教程
|
安全 Linux 网络安全
Python极速搭建局域网文件共享服务器:一行命令实现HTTPS安全传输
本文介绍如何利用Python的http.server模块,通过一行命令快速搭建支持HTTPS的安全文件下载服务器,无需第三方工具,3分钟部署,保障局域网文件共享的隐私与安全。
3350 0
|
存储 监控 API
【Azure App Service】分享使用Python Code获取App Service的服务器日志记录管理配置信息
本文介绍了如何通过Python代码获取App Service中“Web服务器日志记录”的配置状态。借助`azure-mgmt-web` SDK,可通过初始化`WebSiteManagementClient`对象、调用`get_configuration`方法来查看`http_logging_enabled`的值,从而判断日志记录是否启用及存储方式(关闭、存储或文件系统)。示例代码详细展示了实现步骤,并附有执行结果与官方文档参考链接,帮助开发者快速定位和解决问题。
455 22
|
NoSQL MongoDB 数据库
数据库数据恢复—MongoDB数据库数据恢复案例
MongoDB数据库数据恢复环境: 一台操作系统为Windows Server的虚拟机上部署MongoDB数据库。 MongoDB数据库故障: 工作人员在MongoDB服务仍然开启的情况下将MongoDB数据库文件拷贝到其他分区,数据复制完成后将MongoDB数据库原先所在的分区进行了格式化操作。 结果发现拷贝过去的数据无法使用。管理员又将数据拷贝回原始分区,MongoDB服务仍然无法使用,报错“Windows无法启动MongoDB服务(位于 本地计算机 上)错误1067:进程意外终止。”
|
缓存 NoSQL Linux
在CentOS 7系统中彻底移除MongoDB数据库的步骤
以上步骤完成后,MongoDB应该会从您的CentOS 7系统中被彻底移除。在执行上述操作前,请确保已经备份好所有重要数据以防丢失。这些步骤操作需要一些基本的Linux系统管理知识,若您对某一步骤不是非常清楚,请先进行必要的学习或咨询专业人士。在执行系统级操作时,推荐在实施前创建系统快照或备份,以便在出现问题时能够恢复到原先的状态。
1398 79
|
存储 NoSQL MongoDB
MongoDB数据库详解-针对大型分布式项目采用的原因以及基础原理和发展-卓伊凡|贝贝|莉莉
MongoDB数据库详解-针对大型分布式项目采用的原因以及基础原理和发展-卓伊凡|贝贝|莉莉
522 8
MongoDB数据库详解-针对大型分布式项目采用的原因以及基础原理和发展-卓伊凡|贝贝|莉莉