欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

Python中使用Flask、MongoDB搭建簡(jiǎn)易圖片服務(wù)器

系統(tǒng) 1817 0

1、前期準(zhǔn)備

通過(guò) pip 或 easy_install 安裝了 pymongo 之后, 就能通過(guò) Python 調(diào)教 mongodb 了.
接著安裝個(gè) flask 用來(lái)當(dāng) web 服務(wù)器.

當(dāng)然 mongo 也是得安裝的. 對(duì)于 Ubuntu 用戶, 特別是使用 Server 12.04 的同學(xué), 安裝最新版要略費(fèi)些周折, 具體說(shuō)是

            
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

          

如果你跟我一樣覺(jué)得讓通過(guò)上傳文件名的后綴判別用戶上傳的什么文件完全是捏著山藥當(dāng)小黃瓜一樣欺騙自己, 那么最好還準(zhǔn)備個(gè) Pillow 庫(kù)

復(fù)制代碼 代碼如下:

pip install Pillow

或 (更適合 Windows 用戶)

復(fù)制代碼 代碼如下:

easy_install Pillow

2、正片

2.1 Flask 文件上傳

Flask 官網(wǎng)上那個(gè)例子居然分了兩截讓人無(wú)從吐槽. 這里先弄個(gè)最簡(jiǎn)單的, 無(wú)論什么文件都先弄上來(lái)

            
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 '''
  
  
  
  
            
''' if __name__ == '__main__': app.run(port=7777)

注: 在 upload 函數(shù)中, 使用 flask.request.files[KEY] 獲取上傳文件對(duì)象, KEY 為頁(yè)面 form 中 input 的 name 值

因?yàn)槭窃诤笈_(tái)輸出內(nèi)容, 所以測(cè)試最好拿純文本文件來(lái)測(cè).

2.2 保存到 mongodb

如果不那么講究的話, 最快速基本的存儲(chǔ)方案里只需要

            
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('/')


          

把內(nèi)容塞進(jìn)一個(gè)? bson.binary.Binary? 對(duì)象, 再把它扔進(jìn) mongodb 就可以了.

現(xiàn)在試試再上傳個(gè)什么文件, 在 mongo shell 中通過(guò)? db.files.find() 就能看到了.

不過(guò) content? 這個(gè)域幾乎肉眼無(wú)法分辨出什么東西, 即使是純文本文件, mongo 也會(huì)顯示為 Base64 編碼.

2.3 提供文件訪問(wèn)

給定存進(jìn)數(shù)據(jù)庫(kù)的文件的 ID (作為 URI 的一部分), 返回給瀏覽器其文件內(nèi)容, 如下

            
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/
            
              ')
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? 函數(shù)會(huì)跳轉(zhuǎn)到對(duì)應(yīng)的文件瀏覽頁(yè). 這樣一來(lái), 文本文件內(nèi)容就可以正常預(yù)覽了, 如果不是那么挑剔換行符跟連續(xù)空格都被瀏覽器吃掉的話.

2.4 當(dāng)找不到文件時(shí)

有兩種情況, 其一, 數(shù)據(jù)庫(kù) ID 格式就不對(duì), 這時(shí) pymongo 會(huì)拋異常? bson.errors.InvalidId ; 其二, 找不到對(duì)象 (!), 這時(shí) pymongo 會(huì)返回? None .
簡(jiǎn)單起見(jiàn)就這樣處理了

            
@app.route('/f/
            
              ')
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

從現(xiàn)在開(kāi)始要對(duì)上傳的文件嚴(yán)格把關(guān)了, 文本文件, 狗與剪刀等皆不能上傳.
判斷圖片文件之前說(shuō)了我們動(dòng)真格用 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']


          

然后試試上傳文本文件肯定虛, 傳圖片文件才能正常進(jìn)行. 不對(duì), 也不正常, 因?yàn)閭魍晏D(zhuǎn)之后, 服務(wù)器并沒(méi)有給出正確的 mimetype, 所以仍然以預(yù)覽文本的方式預(yù)覽了一坨二進(jìn)制亂碼.
要解決這個(gè)問(wèn)題, 得把 MIME 一并存到數(shù)據(jù)庫(kù)里面去; 并且, 在給出文件時(shí)也正確地傳輸 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/
            
              ')
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)


            
          

當(dāng)然這樣的話原來(lái)存進(jìn)去的東西可沒(méi)有 mime 這個(gè)屬性, 所以最好先去 mongo shell 用? db.files.drop()? 清掉原來(lái)的數(shù)據(jù).

2.6 根據(jù)上傳時(shí)間給出 NOT MODIFIED
利用 HTTP 304 NOT MODIFIED 可以盡可能壓榨與利用瀏覽器緩存和節(jié)省帶寬. 這需要三個(gè)操作

1)、記錄文件最后上傳的時(shí)間
2)、當(dāng)瀏覽器請(qǐng)求這個(gè)文件時(shí), 向請(qǐng)求頭里塞一個(gè)時(shí)間戳字符串
3)、當(dāng)瀏覽器請(qǐng)求文件時(shí), 從請(qǐng)求頭中嘗試獲取這個(gè)時(shí)間戳, 如果與文件的時(shí)間戳一致, 就直接 304

體現(xiàn)為代碼是

            
import datetime
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,
     time=datetime.datetime.utcnow(),
  )
  db.files.save(c)
  return c['_id']
@app.route('/f/
            
              ')
def serve_file(fid):
  try:
    f = db.files.find_one(bson.objectid.ObjectId(fid))
    if f is None:
      raise bson.errors.InvalidId()
    if flask.request.headers.get('If-Modified-Since') == f['time'].ctime():
      return flask.Response(status=304)
    resp = flask.Response(f['content'], mimetype='image/' + f['mime'])
    resp.headers['Last-Modified'] = f['time'].ctime()
    return resp
  except bson.errors.InvalidId:
    flask.abort(404)


            
          

然后, 得弄個(gè)腳本把數(shù)據(jù)庫(kù)里面已經(jīng)有的圖片給加上時(shí)間戳.
順帶吐個(gè)槽, 其實(shí) NoSQL DB 在這種環(huán)境下根本體現(xiàn)不出任何優(yōu)勢(shì), 用起來(lái)跟 RDB 幾乎沒(méi)兩樣.

2.7 利用 SHA-1 排重

與冰箱里的可樂(lè)不同, 大部分情況下你肯定不希望數(shù)據(jù)庫(kù)里面出現(xiàn)一大波完全一樣的圖片. 圖片, 連同其 EXIFF 之類的數(shù)據(jù)信息, 在數(shù)據(jù)庫(kù)中應(yīng)該是惟一的, 這時(shí)使用略強(qiáng)一點(diǎn)的散列技術(shù)來(lái)檢測(cè)是再合適不過(guò)了.

達(dá)到這個(gè)目的最簡(jiǎn)單的就是建立一個(gè)? SHA-1? 惟一索引, 這樣數(shù)據(jù)庫(kù)就會(huì)阻止相同的東西被放進(jìn)去.

在 MongoDB 中表中建立惟一 索引 , 執(zhí)行 (Mongo 控制臺(tái)中)

復(fù)制代碼 代碼如下:

db.files.ensureIndex({sha1: 1}, {unique: true})

如果你的庫(kù)中有多條記錄的話, MongoDB 會(huì)給報(bào)個(gè)錯(cuò). 這看起來(lái)很和諧無(wú)害的索引操作被告知數(shù)據(jù)庫(kù)中有重復(fù)的取值 null (實(shí)際上目前數(shù)據(jù)庫(kù)里已有的條目根本沒(méi)有這個(gè)屬性). 與一般的 RDB 不同的是, MongoDB 規(guī)定 null, 或不存在的屬性值也是一種相同的屬性值, 所以這些幽靈屬性會(huì)導(dǎo)致惟一索引無(wú)法建立.

解決方案有三個(gè):

1)刪掉現(xiàn)在所有的數(shù)據(jù) (一定是測(cè)試數(shù)據(jù)庫(kù)才用這種不負(fù)責(zé)任的方式吧!)
2)建立一個(gè) sparse 索引, 這個(gè)索引不要求幽靈屬性惟一, 不過(guò)出現(xiàn)多個(gè) null 值還是會(huì)判定重復(fù) (不管現(xiàn)有數(shù)據(jù)的話可以這么搞)
3)寫(xiě)個(gè)腳本跑一次數(shù)據(jù)庫(kù), 把所有已經(jīng)存入的數(shù)據(jù)翻出來(lái), 重新計(jì)算 SHA-1, 再存進(jìn)去
具體做法隨意. 假定現(xiàn)在這個(gè)問(wèn)題已經(jīng)搞定了, 索引也弄好了, 那么剩是 Python 代碼的事情了.

            
import hashlib
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)
  sha1 = hashlib.sha1(content.getvalue()).hexdigest()
  c = dict(
    content=bson.binary.Binary(content.getvalue()),
    mime=mime,
    time=datetime.datetime.utcnow(),
    sha1=sha1,
  )
  try:
    db.files.save(c)
  except pymongo.errors.DuplicateKeyError:
    pass
  return c['_id']


          

在上傳文件這一環(huán)就沒(méi)問(wèn)題了. 不過(guò), 按照上面這個(gè)邏輯, 如果上傳了一個(gè)已經(jīng)存在的文件, 返回? c['_id']? 將會(huì)是一個(gè)不存在的數(shù)據(jù) ID. 修正這個(gè)問(wèn)題, 最好是返回? sha1 , 另外, 在訪問(wèn)文件時(shí), 相應(yīng)地修改為用文件 SHA-1 訪問(wèn), 而不是用 ID.
最后修改的結(jié)果及本篇完整源代碼如下 :

            
import hashlib
import datetime
import flask
import pymongo
import bson.binary
import bson.objectid
import bson.errors
from cStringIO import StringIO
from PIL import Image
app = flask.Flask(__name__)
app.debug = True
db = pymongo.MongoClient('localhost', 27017).test
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)
  sha1 = hashlib.sha1(content.getvalue()).hexdigest()
  c = dict(
    content=bson.binary.Binary(content.getvalue()),
    mime=mime,
    time=datetime.datetime.utcnow(),
    sha1=sha1,
  )
  try:
    db.files.save(c)
  except pymongo.errors.DuplicateKeyError:
    pass
  return sha1
@app.route('/f/
            
              ')
def serve_file(sha1):
  try:
    f = db.files.find_one({'sha1': sha1})
    if f is None:
      raise bson.errors.InvalidId()
    if flask.request.headers.get('If-Modified-Since') == f['time'].ctime():
      return flask.Response(status=304)
    resp = flask.Response(f['content'], mimetype='image/' + f['mime'])
    resp.headers['Last-Modified'] = f['time'].ctime()
    return resp
  except bson.errors.InvalidId:
    flask.abort(404)
@app.route('/upload', methods=['POST'])
def upload():
  f = flask.request.files['uploaded_file']
  sha1 = save_file(f)
  return flask.redirect('/f/' + str(sha1))
@app.route('/')
def index():
  return '''
  
  
  
  
              
''' if __name__ == '__main__': app.run(port=7777)


3、REF

Developing RESTful Web APIs with Python, Flask and MongoDB

http://www.slideshare.net/nicolaiarocci/developing-restful-web-apis-with-python-flask-and-mongodb

https://github.com/nicolaiarocci/eve


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 奇米视频888| 午夜大片免费男女爽爽影院久久 | 天天拍夜夜添久久精品中文 | 欧日韩视频 | 久久aⅴ国产欧美74aaa | 亚洲av毛片一区二区久久 | 国产香蕉免费精品视频 | 亚洲综合一二三区 | 精品久久九九 | 免费精品美女久久久久久久久久 | 欧美1区2区 | 波多野结衣全部系列在线观看 | 97久久超碰| 精品专区 | 日本黄页网址 | 亚洲精品黄色 | 国产亚洲欧美在线 | 播播成人网 | 天天视频在线播放观看视频 | 一个色综合亚洲伊人久久 | 久草手机在线视频 | 欧美 亚洲 另类 激情 另类 | 黄色短视频在线免费观看 | 欧美三级欧美一级 | 日产国产欧美视频一区精品 | 亚洲一区中文字幕 | 国产亚洲精品sese在线播放 | 黄色a视频 | 国产尤物在线观看一区二区 | 三级中文字幕 | 插入综合网 | 欧美金妇欧美乱妇视频 | 99久久精品国产一区二区成人 | 欧美久久综合性欧美 | 男人的天堂久久 | 美女污直播 | 又黄又爽的成人免费网站 | 成人在线视频精品 | 国产精品天天干 | 国产乱码精品一区二区三区中文 | 精品欧美一区二区在线看片 |