博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python中使用Flask、MongoDB搭建简易图片服务器
阅读量:6994 次
发布时间:2019-06-27

本文共 9522 字,大约阅读时间需要 31 分钟。

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

你可能感兴趣的文章
我的友情链接
查看>>
linux学习笔记-基础部分
查看>>
我的友情链接
查看>>
Android 下使用 JSON 实现 HTTP 请求
查看>>
运营商中的运营商(CSC)---次级ISP没有运行MPLS
查看>>
我的友情链接
查看>>
修改 firefox 缓存位置
查看>>
MFC鼠标事件
查看>>
ShellExecute如何使用管理员身份执行
查看>>
企业流程管理实践的创新方法——沙盘演练
查看>>
python 读配置文件模块
查看>>
mysql 主主互备快速配置(备忘留存)
查看>>
我的友情链接
查看>>
工具及文章收集
查看>>
python遍历文件夹的方法
查看>>
C语言库函数_-_C开头
查看>>
每年比赛的感想!!!
查看>>
内置参数
查看>>
squid防盗链配置
查看>>
angular03
查看>>