Как отобразить ImageGridFSProxy в HTML?

У меня есть несколько изображений, хранящихся в MongoDB, и я хотел бы отобразить их сейчас на веб-странице. Я использую Flask для своего приложения. Запись имеет двоичное поле с именем «полезная нагрузка».

class BinaryFile(mongo.Document):
    created_at = mongo.DateTimeField(default=datetime.datetime.now, required=True)
    file_name = mongo.StringField(max_length=255, required=True)
    payload = mongo.ImageField(required=False)

Я хочу отобразить полезную нагрузку. Любые подсказки о том, как это сделать? я пробовал

<img src="{{ image.payload }}"/>

который просто дает мне неработающую ссылку. Затем источник страницы читает

<img src="&lt;ImageGridFsProxy: None&gt;"/>

Если я попытаюсь

<img src="{{ image.payload.read() }}"/>

я получил

UnicodeDecodeError: 'ascii' codec can't decode byte 0x89 in position 0: ordinal not in range(128)

Типа запутался. Есть идеи? Спасибо!


person Brian Dolan    schedule 01.02.2015    source источник


Ответы (2)


image.payload.read() возвращает необработанные данные изображения, и это то, что нам нужно, за исключением того, что мы не хотим помещать их в атрибут src тега IMG.

Что мы хотим, так это использовать необработанные данные изображения как изображение и поместить URL-адрес этого изображения в атрибут src.

Вот один пример того, как это можно сделать с помощью временного файла. И это, скорее всего, то решение, которое вам нужно.

from tempfile import NamedTemporaryFile
from shutil import copyfileobj

tempFileObj = NamedTemporaryFile(mode='w+b',suffix='jpg')
copyfileobj(image.payload,tempFileObj)
tempFileObj.seek(0,0)

А затем подайте файл в представлении

from flask import send_file

@app.route('/path')
def view_method():
    response = send_file(tempFileObj, as_attachment=False, attachment_filename='myfile.jpg')
    return response

Возможно, можно отправить данные изображения прямо из объекта ImageGridFSProxy и пропустить временный файл, но я не уверен в этом.

----------

Теперь, когда вы сделали свой код, я опубликую так, как я бы это сделал. И как я пытался объяснить. :)

Вот мой app.py.

from flask import Flask, send_file, render_template
import mongoengine as mo

app = Flask(__name__)
c = mo.connection.connect('localhost')

@app.route('/')
def index():
    images = MyDoc.objects.all()
    return render_template('template.html', images=images)

# Separate view for the images
@app.route('/image/<img_name>')
def image(img_name):
    image = MyDoc.objects(file_name=img_name).first()
    # This is where the tempfile stuff would have been if it would
    # have been needed.
    if image:
        return send_file(image.payload, mimetype='image')
    else:
        return "404" # might want to return something real here too

class MyDoc(mo.Document):
    file_name = mo.StringField(max_length=255, required=True)
    payload = mo.ImageField(required=True)

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

И вот шаблон.

<html>
<body>
<div>
    This is a body!
    <div>
        {% if images %}
           {% for image in images %}
             {{ image.file_name }}
             <img src="/image/{{ image.file_name }}" />
             I'm an image!<br><br>
           {% endfor %}
        {% endif %}

    </div>
</div>
</body>
</html>

Вся эта штука с tempfile была ненужной, так как полезная нагрузка могла быть отправлена ​​напрямую с send_file. Миметип был необходим, чтобы сообщить браузеру, что «это изображение», и браузер должен просто показать его, а не загружать. Это был не as_attachment, как я подозревал ранее.

Таким образом, нет необходимости сохранять файлы с tempfile или в каталог, где они будут обслуживаться статически.

person Sevanteri    schedule 01.02.2015
comment
Спасибо, я попробую. Кажется неэффективным сохранять временный файл. Кроме того, я хочу отобразить изображение, и похоже, что метод send_file прикрепляет его как загрузку. Наконец, можете ли вы сделать несколько файлов таким образом? Я ценю вашу помощь! - person Brian Dolan; 01.02.2015
comment
Ах, может быть, это as_attachment=True причина, по которой он начинает его загружать. Переключите это на False, и это должно работать. - person Sevanteri; 02.02.2015
comment
Вы можете сделать несколько файлов с этим, присвоив изображениям некоторые идентификаторы и ссылаясь на них в представлении, маршрутизируя что-то вроде этого: @app.route('/image/<int:image_id>'), а затем получая правильное изображение с идентификатором. - person Sevanteri; 02.02.2015
comment
Если я правильно помню, Mongo уже дает идентификаторы документам. Вы могли бы использовать их. - person Sevanteri; 02.02.2015
comment
Я играл с этим и обновлю сегодня днем. Я действительно ценю вашу помощь! - person Brian Dolan; 03.02.2015
comment
@BrianDolan Я отредактировал свой пост, чтобы включить то, как я бы это сделал. :) - person Sevanteri; 04.02.2015

Большое спасибо @sevanteri за то, что помогли мне начать. Для потомков, это то, что я в итоге сделал

Вид:

@app.route('/index')
def index():
    images = BinaryFile.objects.all()
    for image in images:
        # This was the temp file idea, abandoned...
        # tmpImageFile = NamedTemporaryFile(mode="w+b", suffix="jpg", delete=False, dir="app/static/temporary_files")
        # copyfileobj(image.payload,tmpImageFile)
        # tmpImageFile.seek(0,0)
        with open(app.config['TEMPORARY_FILE_DIR'] + "/" + image.file_name, 'wb') as f:
            f.write(image.payload.read())
        # image.temporary_file_location = app.config['TEMPORARY_FILE_DIR'] + "/" + image.file_name
        image.temporary_file_location = "static/tmp/" + image.file_name


    return render_template('dashboard.html', images=images)

HTML:

{% block content %}
<div>
    This is a body!
    <div>
        {% if images %}
           {% for image in images %}
             {{ image.file_name }}
             <img src="{{ image.temporary_file_location }}"/>
             I'm an image!<br><br>
           {% endfor %}
        {% endif %}

    </div>
</div>

{% endblock %}

модель:

import datetime
from app import mongo
from flask import url_for

class BinaryFile(mongo.Document):
    created_at = mongo.DateTimeField(default=datetime.datetime.now, required=True)
    file_name = mongo.StringField(max_length=255, required=True)
    payload = mongo.ImageField(required=False)

    def get_absolute_url(self):
        return url_for('post', kwargs={"binary_file": self.file_name})

    def __unicode__(self):
        return self.title

Так что это отстой, потому что я использую команду копирования. Это удовлетворило мой POC сохранения изображений в монго, а затем их отображения, и я позволю своим инженерам разобраться с управлением фактическими файлами. Они умнее меня.

Спасибо ТАК!

person Brian Dolan    schedule 03.02.2015