Как вернуть изображение в HTTP-ответ с помощью CherryPy

У меня есть код, который генерирует Cairo ImageSurface, и я выставляю его так:

def preview(...):
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
    ...
    cherrypy.response.headers['Content-Type'] = "image/png"
    return surface.get_data()
preview.exposed = True

Это не работает (браузеры сообщают, что изображение содержит ошибки).

Я проверил, что surface.write_to_png('test.png') работает, но я не уверен, куда сбрасывать данные, чтобы вернуть их. Я предполагаю, что какой-то файлоподобный объект? Согласно документации pycairo, get_data() возвращает буфер. Я также сейчас пробовал:

tempf = os.tmpfile()
surface.write_to_png(tempf)
return tempf

Кроме того, лучше ли создать и хранить этот образ в памяти (как я пытаюсь сделать) или записать его на диск в виде временного файла и обслуживать его оттуда? Мне изображение нужно только один раз, потом его можно выбросить.


person colinmarc    schedule 17.06.2010    source источник
comment
Как насчет write_to_png_stream? cairographics.org/documentation/cairomm/reference/   -  person Pekka    schedule 17.06.2010
comment
pycairo, похоже, не раскрывает этот метод... cairographics.org/documentation/pycairo/2/reference/   -  person colinmarc    schedule 17.06.2010


Ответы (4)


Добавьте эти импорты:

from cherrypy.lib import file_generator
import StringIO

а затем выполните следующее:

def index(self):
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
    cherrypy.response.headers['Content-Type'] = "image/png"

    buffer = StringIO.StringIO()
    surface.write_to_png(buffer)
    buffer.seek(0)

    return file_generator(buffer)

Кроме того, если вы обслуживаете отдельный файл (т. е. он не является частью веб-страницы) и не хотите, чтобы он отображался в браузере, а скорее рассматривался как файл для сохранения на диске, вам нужен еще один заголовок:

cherrypy.response.headers['Content-Disposition'] = 'attachment; filename="file.png"'

Кроме того, лучше ли создать и хранить этот образ в памяти (как я пытаюсь сделать) или записать его на диск в виде временного файла и обслуживать его оттуда? Мне изображение нужно только один раз, потом его можно выбросить.

Если единственное, что вы хотите сделать, это передать этот файл в браузер, нет причин создавать его на диске на сервере. Наоборот, помните, что доступ к жесткому диску снижает производительность.

person zifot    schedule 20.06.2010
comment
Работает отлично! Что касается награды, не могли бы вы объяснить мне, почему? - person colinmarc; 21.06.2010
comment
Отличный ответ, zifot - я думаю, ключ использует write_to_png, который преобразует необработанные данные в формат PNG. Простое использование Surface.get_data() не будет работать, потому что оно не хранится внутри как данные PNG (это ARGB_32). - person Marc Novakowski; 21.06.2010
comment
@Марк Новаковски - Спасибо. Вы правы, ImageSurface хранит данные в памяти в одном из описанных здесь форматов: cairographics.org/manual/. Формат выходного файла — отдельная история. - person zifot; 21.06.2010
comment
@colinmarc - AFAIK, если у вашего вопроса есть вознаграждение, то и его присуждение, и принятие ответа (которые теперь являются отдельными действиями) возможны не ранее, чем через два дня после создания вознаграждения. - person zifot; 21.06.2010
comment
Большое спасибо, я действительно застрял там на некоторое время. знак равно - person colinmarc; 21.06.2010

Вы терпите неудачу из-за того, что не понимаете, как работает surface.get_data(). Вы пытаетесь вернуть mime-тип image/png, но surface.get_data() возвращает простое растровое изображение (не является растровым файлом Windows .BMP с заголовком), которое представляет собой обычный дамп изображения с «виртуального экрана» (поверхность)

Как это:

0000010000
0000101000
0001000100
0010000010
0001000100
0000101000
0000010000
person Vitold S.    schedule 25.01.2011

Вы пробовали return str(surface.get_data())?

person fumanchu    schedule 17.06.2010

Попробуйте это для подхода "файл в памяти"

return StringIO.StringIO(surface.get_data())
person estin    schedule 18.06.2010