Как лучше хранить много изображений для веб-приложения?

Ссылка скопирована
1 ответ

Приветствую!
Столкнулся с новой для себя задачей, и показалось, что правильным будет не только поискать способы решения в открытом доступе, но и обратиться за советом к кому-то с опытом в решении подобного вопроса.

Есть веб-приложение (веб-интерфейс + API), в качестве СУБД используется Postgres.
Пользователи в частности загружают на сервер изображения, после этого их просматривают/скачивают, также относительно редко само приложение использует эти изображения для ряда операций (без изменения).
Пусть условно со стороны пользователей это будет 1000-2000 запросов на отправку изображений ежедневно и 3000-5000 на получение, размер 1 изображения в среднем 4-5Мб. Предположим с оптимизмом, что объём информации со временем будет расти.

Очень важно хранить все изображения в исходном качестве, не терять их, а также иметь возможность разграничивать к ним доступ среди пользователей приложения. Чуть менее важно уметь отдавать изображения пользователю за разумное время.
Изначально пользователей было немного, наплыва данных не было, и без каких-либо проблем изображения хранились в БД в отдельной таблице в колонке с типом bytea.

На данный момент объём изображений приближается к 1Тб и стремительно растёт, при этом занимают они >90% хранимой информации.
Встаёт вопрос о том, как лучше, и надо ли, реорганизовать хранение изображений. Хочется минимизировать проблемы при работе с БД (процесс создания бекапов и производительность в целом), и потенциальные замедления в доставке изображений до пользователей. И главное, желательно, забыть на долгое время об описанной проблеме :)

До этого не сталкивался с задачами такого рода, поэтому достаточно слабо представляю варианты выбора и их последствия в долгосрочной перспективе.
Имеет ли смысл пытаться дальше расширять дисковое пространство и далее хранить изображения в базе?
Сразу перевозить изображения в облачное объектное хранилище? Позволяет ли оно решить вопрос разграничения доступа на уровне пользователей приложения так, чтобы само приложение не ходило грузить данные с облака?
Либо же какой вариант может быть ещё лучше? :)

Дополнительно:

Хранить файлы в базе с самого начала была так себе затея.

Нормально хранить в S3 (как стороннем, так и самостоятельно поднятым. Легко масштабировать - поднимаем ещё один инстанс, другого поставщика итдитп. В базе храним ссылку на файл. Можно регорганизовать сервис так, что он будет отдавать клиентскому приложению presigned url на файл ограниченного срока действия (например, 5 минут), чтобы утекание ссылки не создавало больших рисков. Так как контент отдаваемого файла при этом не прокачивается через бэкенд, можно избежать лишней нагрузки на своё приложение. И база не страдает, потому что извлечь ссылку на файл можно быстро.

Но это необязательно, можно и просто на диске. Но не в базе.

в БД обычно хранится "ссылка" хеш на файл, а сам файл может хранится либо просто на диске в системе, либо на подключенном S3 к примеру, это уже как по деньгам удобству итд...

  • на подключенном S3 к примеру

    Ссылку на пример добавьте пожалуйста.

    На сколько я понимаю, это нужно писать API-сервер для хранения и раздачи файлов на отдельном сервере?

  • My1Name, у меня нет примеров. Это направление мысли
  • My1Name,

    Ссылку на пример добавьте пожалуйста.

    Пример чего? S3 провайдер обычно описывает стандартные эндпоинты апи, а для всех популярных движков есть готовые пакеты для драйвера хранилища с документацией.

  • ThunderCat, Тут прочитал, что я в общем-то правильно понимаю, но это сам принцип устройства объектного хранилища S3: Это HTTP-API, который позволяет загружать, получать и удалять данные по имени.

    Пример чего?

    Я не могу понять, как работать с S3 программно, в моём случае средствами java? Мне не нужен веб-интерфейс в картинках...

    Мне надо что-то типа: "качаем драйвер с Maven repository; устанавливаем логин/пароль администратора. Отправляем файл таким-то образом... Получаем ссылку на файл таким-то образом..."

  • My1Name, вот описание API Yandex Cloud Object Storage - это копия AWS S3 , там есть примеры и документация.
    https://cloud.yandex.com/en/docs/storage/s3/
  • My1Name, как я понимаю, Вам нужно искать для Java что-то по ключевым словам "java amazon s3", и вы получите готовое искомое решение, которое по идее должно быть совместимо с теми же решениями от Яндекса и т.д.
  • не надо на S3 хранить и на клауде - оно медленное....

Хранение файлов в бд - самый хреновый вариант из имеющихся. По многим причинам, но основная - файлы должны лежать в файловой системе. Это их естественное состояние. Это быстро, не затратно, не потребляет лишнюю память/процессор и просто удобно. В зависимости от необходимости давать к ним доступ всем или по авторизации, будет либо работа напрямую с статикой через какой-нить нжинкс, либо программно сформированный поток байт через что-то типа пхпшного readfile...

В зависимости от возможностей хостинга, файлы хранят либо локально(в случае например собственного выделенного сервера и наличия райд стойки), либо, как уже написали, в облачных хранилищах, что чаще всего достаточно выгодно в плане денег за объем/трафик, плюс расширение хранилища будет просто отражаться на счете за услуги, а не ложиться ответственностью на вас как владельца сгоревших винтов с данными клиента. АПИ у всех весьма несложные, а драйвера для работы с удаленными файлохранилищами есть практически для всех популярных движков в виде готовых пакетов.

  • Так вроде хранение файлов в бд - не подразумевалось....
  • Dmitry Bay,

    Изначально пользователей было немного, наплыва данных не было, и без каких-либо проблем изображения хранились в БД в отдельной таблице в колонке с типом bytea.

  • ThunderCat, Вечером читал, пропустил среди текста сухого это.

Ответы:

Веб приложения максимально оптимизированы при работе с файлами на диске.
Никакой другой метод не позволит дать такую производительность.

Поэтому - авторизацию доступа делай на уровне веб сервера (вот пример с нормальным oauth)

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

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

Тупой пример - числовой идентификатор переводи в hex, дели на группы по 4 символа и создавай соответствующие каталоги: /images/0d4f/3b00/a841/0d88, тут 0d88 это файл, остальные части - каталоги. Идентификатор соответственно 64-битное число 0x0d4f3b00a8410d88

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

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

  • только надо резать каталоги по два символа, а не по 4, потому что любая ФС плохо работает с десятками тысяч записей в одном каталоге. ну просто плохо.
  • hx510b, операционная система прекрасно работает с большим количеством файлов (если ты конечно не выберешь в качестве файловой системы какой-нибудь fat) и я надеюсь мы говорим про linux

    проблемы у пользовательских утилит, даже штатный rm работает отвратительно, если файлов десятки или сотня тысяч (но есть лайфхаки, использовать find).

Чтобы забыть о проблеме на долго конечно лучший выбор отдельное объектное хранилище. Не обязательно это должно быть облачное решение, можно организовать self-hosted S3-совместимое решение с использованием https://min.io/. Это позволит использовать любую готовую библиотеку s3-клиента для работы с файлами (upload/download/delete)

Плюсы - при правильной настройке размер хранилища можно бесконечно расширять и масштабировать/реплицировать.

Вопрос разграничения доступа можно решить тем что для доступа к файлу генерируется временная ссылка с ключем. Вариант хорош тем что раздачей файлов будет заниматься сервис хранения и не надо гонять данные между бэкендом и хранилищем. Бэкенд лишь при запросе файла проверяет права доступа и генерит секретную ссылку на скачивание.

Другой вариант это проксировать все запросы на файлы через приложение: пользователь запросил файл -> бэкенд проверил права доступа -> бэкенд обратился к s3 хранилищу чтобы получить файл -> бэкенд передал файл пользвоателю. Это примерно как было у вас с хранением файлов в базе, пользователь не может напрямую скачать файл из базы, а это за него делает ваше бэкенд приложение.

Много слов, а задача тривиальная. Файлы на сервере хранят как дома. Представь тебе надо хранить фото. Распихал по папкам с названиями и тип-топ. Мало места - надо докупить или удалить лишнее. Надо бэкап значит надо. Это все элементарная логика и больше ничего. Все остальное как ее написать кодом.

  • Спасибо за ответ, очень буду рад, если в итоге всё окажется тривиальным и ограничится элементарной логикой :)
    Много слов было на случай существования конкретных устоявшихся хороших практик при конкретных условиях и проблем различных подходов, на которые стоит обратить внимание.

Все комментаторы выше далеки от реальности, хочешь в базе, хочешь в папках на дисковой подсистеме, хочешь в матрицах любых размерностей в оперативной памяти храни, как больше нравится и удобнее тебе. Производительность всей хурмы зависит только от реализации её, храниш на диске, складывай по папкам, которые будут служить разделами(индексами) расположения нужного файла с привязкой к чему-то удобному тебе, что быстро и без заморочек ты всегда понимал, как обратится к нужным данным, хранишь в базе, используй таблицу связей, храня в ней разделы, либо используй разделы прям в основной одной таблице, при выборке всегда добавляй связь с разделами для минимизации результатов выборки, используй при поиске ограничения топ, оффсет и прочую хурму, тоже и в оперативке. Файлы на дисковой подсистеме это та же база данных, только на уровне операционной системы и ее приложений, та же индексация, разбивка, долгий поиск и обращения к нужным данным при отсутствии структурированных индексов, для быстрого поиска рекомендую маркировать файлы самостоятельно тегами по которым можно их искать, как пример изображение с цветком, теги цвет, красный цветок, роза, ваза, цветок в вазе, лето, маша, ну суть думаю ясна. Файл dota2.exe теги игруха, дота, pvp, игры, игры на пк, компьютерные игры и т.д.

Нужно решить такую задачу?

Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

Заказать помощь
Лучший ответ
1
Павел Админов Ответ

Для хранения большого количества изображений для веб-приложения лучше всего использовать облачное хранилище, такое как Amazon S3, Google Cloud Storage или Microsoft Azure Blob Storage. Эти сервисы предоставляют надежное и масштабируемое хранилище для ваших данных, а также позволяют легко управлять доступом к изображениям и управлять версиями файлов.

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

Для загрузки изображений в облачное хранилище вы можете использовать SDK или API, предоставляемые провайдером. Например, для Amazon S3 вы можете использовать AWS SDK для PHP. Ниже приведен пример кода на PHP для загрузки изображения в Amazon S3:

use Aws\S3\S3Client;
 
$s3 = new S3Client([
    'version' => 'latest',
    'region' => 'us-west-2',
    'credentials' => [
        'key'    => 'YOUR_AWS_ACCESS_KEY_ID',
        'secret' => 'YOUR_AWS_SECRET_ACCESS_KEY',
    ],
]);
 
$result = $s3->putObject([
    'Bucket' => 'YOUR_BUCKET_NAME',
    'Key'    => 'path/to/image.jpg',
    'Body'   => fopen('/path/to/local/image.jpg', 'rb'),
    'ACL'    => 'public-read',
]);

use Aws\S3\S3Client; $s3 = new S3Client([ 'version' => 'latest', 'region' => 'us-west-2', 'credentials' => [ 'key' => 'YOUR_AWS_ACCESS_KEY_ID', 'secret' => 'YOUR_AWS_SECRET_ACCESS_KEY', ], ]); $result = $s3->putObject([ 'Bucket' => 'YOUR_BUCKET_NAME', 'Key' => 'path/to/image.jpg', 'Body' => fopen('/path/to/local/image.jpg', 'rb'), 'ACL' => 'public-read', ]);

Таким образом, использование облачного хранилища для хранения большого количества изображений для вашего веб-приложения является надежным и эффективным решением.

Другие ответы (0)

Пока нет других ответов. Будьте первым, кто поможет автору.

Ответить на вопрос

комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вам также может быть интересно