zoukankan      html  css  js  c++  java
  • Python中使用Flask、MongoDB搭建简易图片服务器

    转自:http://www.ctolib.com/topics-43840.html

     

    1、前期准备

    通过 pip 或 easy_install 安装了 pymongo 之后, 就能通过 Python 调教 mongodb 了.
    接着安装个 flask 用来当 web 服务器.

    当然 mongo 也是得安装的. 对于 Ubuntu 用户, 特别是使用 Server 12.04 的同学, 安装最新版要略费些周折, 具体说是

    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 官网上那个例子居然分了两截让人无从吐槽. 这里先弄个最简单的, 无论什么文件都先弄上来

    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

    如果不那么讲究的话, 最快速基本的存储方案里只需要

    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 的一部分), 返回给浏览器其文件内容, 如下

    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 .
    简单起见就这样处理了

    @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()  清掉原来的数据.

  • 相关阅读:
    git线上操作
    IDEA快捷方式
    Java 四种线程池
    java 获取当前天之后或之前7天日期
    如何理解AWS 网络,如何创建一个多层安全网络架构
    申请 Let's Encrypt 通配符 HTTPS 证书
    GCE 部署 ELK 7.1可视化分析 nginx
    使用 bash 脚本把 AWS EC2 数据备份到 S3
    使用 bash 脚本把 GCE 的数据备份到 GCS
    nginx 配置 https 并强制跳转(lnmp一键安装包)
  • 原文地址:https://www.cnblogs.com/bonelee/p/6513455.html
Copyright © 2011-2022 走看看