2025, Oct 22 13:01

Plotly в Cloudinary без диска: прямая загрузка из памяти

Как загрузить графики Plotly в Cloudinary без обращения к файловой системе: BytesIO, write_image и base64. Полный пример прямой отправки из памяти на Python.

Потоковая отправка графиков Plotly в Cloudinary без обращения к файловой системе — практичный способ избежать проблем с правами доступа и временных файлов. Ключ в том, чтобы передавать между Plotly и Cloudinary буфер в памяти вместо пути на диске.

Базовый сценарий: запись на диск

Обычно рабочий процесс выглядит так: изображение сохраняется в локальный файл, а затем этот файл загружается:

img_path = "chart.png"
viz_obj.write_image(img_path)
cloudinary.uploader.upload(
    img_path,
    asset_folder="reports",
    public_id="latest_plot",
    overwrite=True
)

Это перестаёт работать, когда среда не может писать на диск или когда вы хотите, чтобы ничего не оставалось на носителе.

Что позволяет обойтись без диска

Возможность опирается на то, что обе стороны принимают файлоподобный объект. write_image в Plotly может писать в записываемый объект, а загрузчик Cloudinary умеет принимать data_stream (буфер массива байтов). Благодаря такой совместимости буфер BytesIO в памяти становится связующим звеном.

Прямая загрузка из буфера в памяти

Создайте буфер в памяти, отрендерьте в него фигуру, верните указатель в начало и передайте буфер в Cloudinary:

import io
buffer_io = io.BytesIO()
viz_obj.write_image(buffer_io, format="png")
buffer_io.seek(0)
cloudinary.uploader.upload(
    buffer_io,
    asset_folder="reports",
    public_id="latest_plot",
    overwrite=True
)

Так весь процесс остаётся в памяти и избавляет от любых проблем с правами на файловую систему.

Альтернатива: строка данных base64

Cloudinary также принимает данные в base64. Можно отрендерить фигуру в буфер, извлечь байты, закодировать их и отправить data URI:

import io
import base64
mem_buf = io.BytesIO()
viz_obj.write_image(mem_buf, format="png")
raw_bytes = mem_buf.getvalue()
b64_bytes = base64.b64encode(raw_bytes)
b64_text = b64_bytes.decode()
data_uri = "data:image/png;base64," + b64_text
cloudinary.uploader.upload(
    data_uri,
    asset_folder="reports",
    public_id="latest_plot",
    overwrite=True
)

Почему это важно

Использование файлоподобного объекта делает поток данных простым, безопасным и быстрым. Не нужны временные файлы, не требуется уборка, а в ограниченных средах меньше компонентов. Если вы уже работаете с другими библиотеками визуализации, принцип тот же: рендерьте в буфер и передавайте этот буфер загрузчику. Например, если используете Altair, при записи в буфер перед загрузкой вместо write_image используйте его метод save.

Итоги

Если ваша цель — не писать на диск, свяжите write_image из Plotly с BytesIO в памяти и передайте этот буфер загрузчику Cloudinary. Когда буфер данных недоступен в вашей интеграции, base64 — рабочая альтернатива. Держите весь поток только в памяти — и вы полностью обойдёте файловые разрешения, сохранив загрузки простыми.

Материал основан на вопросе со StackOverflow от diogenes и ответе пользователя furas.