pymongo使用BSON類型存取(圖片)文件

Mongodb可以使用BSON格式來保存大小不超過16M的二進制文件,很適合用來存放web中的圖片資源,記錄一下操作BSON格式的方法

本地文件寫入Pymongo/Pymongo數據寫回本地文件

"""
@File        : file_demo.py
@Description : 本地文件寫入Pymongo/Pymongo數據寫回本地文件
@Time        : 2020/4/7 23:20
@Author      : DexterLien
@Email       : [email protected]
@Software    : PyCharm
"""
import os

from pymongo import MongoClient

# 連接帶有密碼認證的Mongodb,格式如下:
# mongodb://用戶名:密碼@服務器地址/數據庫名
con = MongoClient('mongodb://username:[email protected]:8017/dexter')

# 使用名爲dexter的數據庫
db = con['dexter']

# 使用名爲users的collection
users = db['users']

# 本地文件以BSON二進制寫入數據庫集合
fpath = os.path.join(os.getcwd(), 'pic.png')
with open(fpath, 'rb') as file:
    users.insert_one({
        'name': 'pic.png',
        'data': file.read()
    })

# 數據庫中的BSON數據寫回文件
ret = users.find_one({'name': 'pic.png'})
with open(os.path.join(os.getcwd(), 'new.png'), 'wb') as file:
    file.write(ret.get('data'))

POST上傳的圖片附件寫入Mongodb與返回

用Flask隨便擼了一個demo,記錄一下主要的思路

"""
@File        : post_pic.py
@Description : POST上傳的圖片附件寫入Mongodb與返回
@Time        : 2020/4/7 23:59
@Author      : DexterLien
@Email       : [email protected]
@Software    : PyCharm
"""
from flask import Flask, request, render_template, jsonify, make_response
from pymongo import MongoClient
from werkzeug.datastructures import FileStorage

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False


def save_to_mongo(file: FileStorage):
    """
    將post請求中的文件對象保存至Mongodb,主要是通過file.stream.read()方法直接將二進制數據讀出來,給Mongodb插入用
    :param file:
    :return:
    """
    con = MongoClient('mongodb://username:[email protected]:8017/dexter')
    db = con['dexter']
    users = db['users']
    users.insert_one({
        'name': file.filename,
        'data': file.stream.read()
    })


@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        return render_template('index.html')

    if request.method == 'POST':
        file = request.files.get('pic')
        save_to_mongo(file)
        return jsonify({
            'code': 0,
            'msg': '上傳成功'
        })


@app.route('/pic/<filename>', methods=['GET'])
def pic(filename):
    con = MongoClient('mongodb://username:[email protected]:8017/dexter')
    db = con['dexter']
    users = db['users']
    data = users.find_one({'name': filename})['data']
    # 使用Mongodb查詢到的BSON數據生成response響應,後面通過設置不同的headers實現不同響應行爲
    response = make_response(data)
    # 在瀏覽器中直接顯示圖片
    response.headers.set('Content-Type', 'image/png')
    # 以文件下載方式返回
    # response.headers.set(
    #     'Content-Disposition', 'attachement', filename=f'{filename}'
    # )
    return response


if __name__ == '__main__':
    app.run(debug=True)

上傳操作前端頁面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{# 注意:Flask中需要指定form的enctype才能將文件封裝發送到post請求中 #}
<form method="post" enctype=multipart/form-data>
    <input name="pic" type="file">
    <button type="submit">上傳</button>
</form>
</body>
</html>

使用Flask直接將圖片二進制數據傳給jinja2模板渲染

主要是使用base64.b64encode()先將Mongodb中的二進制數據進行Base64編碼,然後再對其使用utf-8解碼,這樣就可以得到圖像的Base64類型的字符串數據

import base64
# 省略其他代碼
@bp.route('/users')
def users_view():
    _users = users.find({})
    data = [u for u in _users]
    for d in data:
        d['mgcz'] = base64.b64encode(d['mgcz']).decode('utf-8')
    return render_template('users.html', users=data)

jinja2模板中使用<img src="data:;base64,{{ user.mgcz | safe }}">這樣格式的標籤來渲染Base64格式字符串數據的圖片

<div>
     <ul>
         {% for user in users %}
             <li>
                 {{ user.xm }}
                 <img src="data:;base64,{{ user.mgcz | safe }}">
             </li>
         {% endfor %}
     </ul>
 </div>

與傳統文件方式存儲圖片佔用空間對比

做了一個簡單的測試:
傳統文件方式存放的圖片new.webp文件實際大小:
在這裏插入圖片描述
使用BSON類型向MongoDB中寫入該圖片文件的前後服務器上數據庫文件大小對比(單位byte字節)

[email protected]:~/mongodb$ sudo du -sb
336945978       .
[email protected]:~/mongodb$ sudo du -sb
336947156      

相減後服務器上MongoDB存儲這個圖片數據只用了1,178個字節,比傳統文件方式佔用的60,362字節大小減少了98%,簡直太叼了有木有!!!感覺這樣對比是不是有點不準確呢?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章