Введение

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

Оптимальным выходом из данной ситуации является представление изображения в виде массива байтов byte[]

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

Любой файл можно рассматривать как набор байтов, поэтому массив байтов является результатом чтения файла в виде двоичных данных. Файл также может быть декодирован как определенный формат, например, текстовый файл, результатом которого является строка, или формат сжатого изображения (JPEG, GIF, PNG и т. д.), результатом которого является Bitmap.

Программное обеспечение

Microsoft SQL Server


Microsoft SQL Server является одной из наиболее популярных систем управления базами данных (СУБД) в мире. Данная СУБД подходит для самых различных проектов: от небольших приложений до больших высоконагруженных проектов.

SQL Server был создан компанией Microsoft. Первая версия вышла в 1987 году. А текущей версией является версия 2019, которая вышла в 2019 году и которая будет использоваться в текущем руководстве.

SQL Server долгое время был исключительно системой управления базами данных для Windows, однако начиная с версии 16 эта система доступна и на Linux.

SQL Server характеризуется такими особенностями как:

  • Производительность. SQL Server работает очень быстро.

  • Надежность и безопасность. SQL Server предоставляет шифрование данных.

  • Простота. С данной СУБД относительно легко работать и вести администрирование.

Центральным аспектом в MS SQL Server, как и в любой СУБД, является база данных. База данных представляет хранилище данных, организованных определенным способом. Нередко физически база данных представляет файл на жестком диске, хотя такое соответствие необязательно. Для хранения и администрирования баз данных применяются системы управления базами данных (database management system) или СУБД (DBMS). И как раз MS SQL Server является одной из такой СУБД.

SQL Server Downloads | Microsoft

microsoft.com


SQL Server Management Studio

Для удобного управления базами данных и различными опциями и настройками в MS SQL Server установим специальное средство администрирования, которое называется SQL Server Management Studio (SSMS). Данную программу можно использовать для создания баз данных и их таблиц, написания и выполнения запросов к бд, а также для много другого.

Рис.1 MSSMS
Рис.1 MSSMS

Download SQL Server Management Studio (SSMS) - SQL Server Management Studio (SSMS)

learn.microsoft.com

Реализация

Приступим к реализации поставленное задачи, а именно сохранение изображение в БД.

Шаг.1 Создадим простой проект WPF, назовем его Image Test WPF (Рис.2)

Рис.2 Создание проекта
Рис.2 Создание проекта

Шаг.2 Добавим на форму компонент Image, установим его атрибут name как «image», а также зададим его ширину и высоту(Рис.3)

Рис.3 Добавление компонента image
Рис.3 Добавление компонента image
Код
<Image x:Name="image" Width="200" Height="200"></Image>

Шаг.3 Добавим кнопку для выбора изображения (Рис.4)

Рис.4 Добавление кнопки
Рис.4 Добавление кнопки
Код
<Button Content="Выбрать изображение" VerticalAlignment="Bottom"></Button>

Шаг.4 Пропишем код для открытия диалогового окна выбора файла

Для этого пропишем событие клика кнопки

Рис.5 клик
Рис.5 клик
Код
 <Button Click="SelectImage" Content="Выбрать изображение" VerticalAlignment="Bottom"></Button>

Два раза нажмем по кнопке и перейдем в метод (Рис.6):

Рис.6 Код обработчика
Рис.6 Код обработчика

Далее пропишем код для вызова диалогового окна выбора файла OpenFIleDialog

Пропишем подключение нужных директив

using Microsoft.Win32;
using System.IO;

Пропишем логику

Рис.7 Логика выбора изображения
Рис.7 Логика выбора изображения
Код
private void SelectImage(object sender, RoutedEventArgs e)
{
      OpenFileDialog openFileDialog = new OpenFileDialog(); // создаем диалоговое окно
      openFileDialog.ShowDialog(); // показываем
      byte[] image_bytes = File.ReadAllBytes(openFileDialog.FileName); // получаем байты выбранного файла
}

Шаг.5 Создадим базу данных

В программе My Sql Management Studio создадим в базе данных таблицу и назовем ее Images (Рис.8)

Рис.8 Таблица БД
Рис.8 Таблица БД

Установим для поля image тип данных varbinary(MAX)

Шаг. 6 Допишем код выбора изображения с использованием библиотеки для сохранения изображения

Скачайте файл с github и поместите в ваш проект (Рис.9)

И в данном классе пропишите библиотеки:

using System.Drawing;

using System.Drawing.Imaging;

using System.IO;

using System.Windows.Media.Imaging;
Рис.9 Структура проекта
Рис.9 Структура проекта

Добавим в проект класс для работы с базой данных

Рис.10 Класс для работа с БД
Рис.10 Класс для работа с БД

Переменную sqlConnection замените на свои данные

Шаг. 7 Добавим еще одну кнопку для загрузки изображения из базы данных (Рис.11)

Рис.11 кнопка загрузки
Рис.11 кнопка загрузки

Шаг. 8 Напишем логику загрузки изображения из БД, а также сохранения

Дополним код выбора изображения так, чтобы он еще и сохранял выбранное фото в БД (Рис.12)

Рис.12 Запись в бд
Рис.12 Запись в бд
private void SelectImage(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog(); // создаем диалоговое окно
            openFileDialog.ShowDialog(); // показываем
            byte[] image_bytes = File.ReadAllBytes(openFileDialog.FileName); // получаем байты выбранного файла

            string connectionString = "server=192.168.140.128;Trusted_Connection=No;DataBase=Test;User=s;PWD=s"; // строка подключения
            using (SqlConnection connection = new SqlConnection(connectionString)) // создаем подключение
            {
                connection.Open(); // откроем подключение
                SqlCommand command = new SqlCommand(); // создадим запрос
                command.Connection = connection; // дадим запросу подключение
                command.CommandText = @"INSERT INTO images VALUES (@ImageData)"; // пропишем запрос
                command.Parameters.Add("@ImageData", SqlDbType.Image, 1000000); 
                command.Parameters["@ImageData"].Value = image_bytes;// скалярной переменной ImageData присвоем массив байтов
                command.ExecuteNonQuery(); // запустим
            }
        }

Пропишем логику загрузки из БД:

Рис.13 Загрузка из БД
Рис.13 Загрузка из БД
 private void LoadImage(object sender, RoutedEventArgs e)
 {
      DataTable matcher_query = SqlModel.Select("SELECT * from Images"); // запрос
      image.Source = ByteImage.Convert(ByteImage.GetImageFromByteArray((byte[])matcher_query.Rows[matcher_query.Rows.Count - 1][0])); 
      // берем из запроса последнюю строку и ее массив байтов
 }

Шаг. 9 Протестируем программу

Расширим окно, чтобы увидеть кнопку выбора изображения

Далее выберем изображение и нажнем загрузить

Как видим все работает!

Объяснение кода загрузки

ByteImage.Convert() - преобразует изображение в понятный для wpf вид

ByteImage.GetImageFromByteArray() - преобразует массив байтов в иозображение

Соответственно конструкция:

  image.Source = ByteImage.Convert(ByteImage.GetImageFromByteArray(массив байтов);

Преобразует массив байтов в изображение, конвертирует его в понятный для wpf вид и устанавливает в компонент image

Пример можно найти тут

Краткий экскурс по работе с библиотекой

Скачайте файл с github и поместите в ваш проект файл

Чтобы перевести массив байтов byte[] в изображение и поместить его в компонент Image нужно соделать следующее:

ByteImage.Convert(ByteImage.GetImageFromByteArray(массив байтов));

Чтобы перевести изображение в массив байтов нужно сделать следующее:

byte[] some_image_bytes = File.ReadAllBytes("image.jpg");

Вывод

Сохранение изображения в БД не такая уж сложная задача. Если использовать написанную мной библиотеку, то код сводится к минимуму , грубо говоря в одну строку.

Комментарии (16)


  1. s207883
    20.11.2022 10:19
    +1

    Почему бы не воспользоваться профильным решением в виде файлового хранилища на cepf/minio/etc? Все библиотеки есть под c#.


    1. RedMooner Автор
      20.11.2022 10:22
      -10

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


      1. Ultra164
        20.11.2022 18:55
        +2

        Давно студентов заставляют писать статьи на хабр?


      1. Eugene75
        20.11.2022 18:55
        +1

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

        Можна было и добавить альтернативное решение.


    1. mayorovp
      20.11.2022 13:10
      +1

      Или же можно использовать FILESTREAM, раз уж всё равно используется MS SQL Server...


  1. ondister
    20.11.2022 11:14
    +3

    Ну и System.Drawing точно тянуть не нужно.


  1. Ztare
    20.11.2022 11:39
    +13

    Изображения почти всегда огромные объекты по меркам шарпа. Возьмите за практику не материализовывать такие штуки в массив. Везде: в файлах, в web запросах, в запросах к бд изображение выгодно пробросить как стрим. Вы будете приятно удивлены результатом и будете вопрошать "почему я не сделал этого раньше" )


  1. vagon333
    20.11.2022 15:26
    +1

    Решение раздувает базу, замедляет работу.
    Смоделируйте нагрузку - сгенерите 100,000 бинарников и замерьте разницу во времени ответа и загрузке проца сервера.
    Используйте FILESTREAM: SQL Server хранит картинки как файлы на диске, а ссылки в базе.


    1. shai_hulud
      20.11.2022 15:39

      А еще можно хранить файлы на ФС, а ссылки в базе :)


      1. vagon333
        20.11.2022 15:44
        +2

        Можно.
        Задача усложняется контролем доступа - в FILESTREAM занимается SQL Server, а в при хранении отдельно - придется заниматься разработчику, включая проблемы безопасности, целостности.
        Проходил и бинарник а таблице, в далекие 00, и файлы по-отдельности, когда миллионы документов, и FILESTREAM.
        Бинарник а таблице - самое неудачное решение. :)


  1. alex-khv
    20.11.2022 16:06
    +3

    О, курсовые на хабр подъехали.


  1. JordanCpp
    20.11.2022 16:49
    -3

    О господи вы ещё пользуетесь SQL Server'ом. О господи они хранят бинари в базе. Продолжите список... :)


    1. Ivan22
      20.11.2022 16:55

      ....еще скажи, они еще не в клауде, и еще не хранят файлы в S3


  1. JordanCpp
    20.11.2022 16:50
    +3

    О господи, в конце 2022 пишут статью как хранить бинари в базе SQL Server. :)


  1. dopusteam
    20.11.2022 18:57
    +2

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

    Кто мешает делать это на сервере?

    Что значит "не всегда"?


  1. Grayver
    21.11.2022 05:04
    +5

    Хранение изображений в БД обычно считается антипаттерном по ряду причин:

    • реляционные базы данных (такие как MS SQL) прежде всего работают в рамках транзакционной модели управления данными - вовлечение BLOB-ов в такую модель снижает её эффективность

    • доставка изображений на фронтенд из БД всегда накладнее, чем доставка из файловых хранилищ

    • доставку изображений на фронтенд сложнее масштабировать (нет возможности использовать CDN)

    • если вам критически важны ваши данные и ваш MS SQL работает в режиме восстановления Full, то все ваши изображения попадут в транзакционный журнал - его размер будет стремительно расти и расходы на его обслуживание возрастут кратно

    • изображения стремительно увеличивают размер БД, что приводит к деградации производительности