Как добавить и выгрузить картинку из Ms Sql

У меня есть БД, где есть столбец с картинками. Как вставить картинку в БД, а потом через какой тип данных её выгрузить в контроллер Asp .net, а потом уже в ангуляр. Насколько я знаю, то это тип данных byte[], но это в на стороне сервера, а как быть в Angular? Если даже поставить any, то картинки просто не загружаются. БД: Модель БД на стороне сервера:
public class Clothes < public int ClothesId < get; set; >public string VendorCode < get; set; >public string Name < get; set; >public byte[] Image < get; set; >public int Amount < get; set; >public int Price < get; set; >>
Модель данных на ангуляре:
export class Clothes
Отслеживать
задан 2 авг 2022 в 22:48
user511657 user511657
а почему булеан?
3 авг 2022 в 3:33
Поправил на произвольный тип.
– user511657
3 авг 2022 в 11:45
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
на стороне MS SQL Server надо получить данные в base64 из вашей колонки varbinary, где записаны картинки. Вот пример конвертации данных:
select ClothesId, Image from openjson( ( select ClothesId ,Image from Clothes for json auto ) ) with(ClothesId int, Image varchar(max))
условия в выборку сами добавьте по конкретному id и тп. для отображения в html нужно будет подставить полученную строку base64: допустим, что
clothes.Image = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
Вставка изображения из файла
Большой двоичный объект (BLOB) можно записать в базу данных в виде двоичных или символьных данных в зависимости от типа поля в источнике данных. Большой двоичный объект — это универсальный термин, который относится к типам данных text , ntext и image , которые обычно содержат документы и изображения.
Чтобы записать значение BLOB в базу данных, выполните соответствующую инструкцию INSERT или UPDATE и передайте значение BLOB в качестве входного параметра (см . раздел Настройка параметров и типов данных параметров). Если ваш большой двоичный объект хранится в виде текста, например поле text SQL Server, большой двоичный объект можно передать в виде строкового параметра. Если большой двоичный объект хранится в двоичном формате, например в поле image SQL Server, массив типа byte можно передать как двоичный параметр.
Пример
В следующем примере кода данные о сотрудниках добавляются в таблицу «Сотрудники» в базе данных Northwind. Фотография сотрудника считывается из файла и добавляется в поле «Фото» в таблице, которая является полем изображения.
Public Shared Sub AddEmployee( _ lastName As String, _ firstName As String, _ title As String, _ hireDate As DateTime, _ reportsTo As Integer, _ photoFilePath As String, _ connectionString As String) Dim photo() as Byte = GetPhoto(photoFilePath) Using connection As SqlConnection = New SqlConnection( _ connectionString) Dim command As SqlCommand = New SqlCommand( _ "INSERT INTO Employees (LastName, FirstName, Title, " & _ "HireDate, ReportsTo, Photo) " & _ "Values(@LastName, @FirstName, @Title, " & _ "@HireDate, @ReportsTo, @Photo)", connection) command.Parameters.Add("@LastName", _ SqlDbType.NVarChar, 20).Value = lastName command.Parameters.Add("@FirstName", _ SqlDbType.NVarChar, 10).Value = firstName command.Parameters.Add("@Title", _ SqlDbType.NVarChar, 30).Value = title command.Parameters.Add("@HireDate", _ SqlDbType.DateTime).Value = hireDate command.Parameters.Add("@ReportsTo", _ SqlDbType.Int).Value = reportsTo command.Parameters.Add("@Photo", _ SqlDbType.Image, photo.Length).Value = photo connection.Open() command.ExecuteNonQuery() End Using End Sub Public Shared Function GetPhoto(filePath As String) As Byte() Dim stream As FileStream = new FileStream( _ filePath, FileMode.Open, FileAccess.Read) Dim reader As BinaryReader = new BinaryReader(stream) Dim photo() As Byte = reader.ReadBytes(stream.Length) reader.Close() stream.Close() Return photo End Function
public static void AddEmployee( string lastName, string firstName, string title, DateTime hireDate, int reportsTo, string photoFilePath, string connectionString) < byte[] photo = GetPhoto(photoFilePath); using (SqlConnection connection = new SqlConnection( connectionString)) SqlCommand command = new SqlCommand( "INSERT INTO Employees (LastName, FirstName, " + "Title, HireDate, ReportsTo, Photo) " + "Values(@LastName, @FirstName, @Title, " + "@HireDate, @ReportsTo, @Photo)", connection); command.Parameters.Add("@LastName", SqlDbType.NVarChar, 20).Value = lastName; command.Parameters.Add("@FirstName", SqlDbType.NVarChar, 10).Value = firstName; command.Parameters.Add("@Title", SqlDbType.NVarChar, 30).Value = title; command.Parameters.Add("@HireDate", SqlDbType.DateTime).Value = hireDate; command.Parameters.Add("@ReportsTo", SqlDbType.Int).Value = reportsTo; command.Parameters.Add("@Photo", SqlDbType.Image, photo.Length).Value = photo; connection.Open(); command.ExecuteNonQuery(); >> public static byte[] GetPhoto(string filePath)
См. также раздел
- Использование команд для изменения данных
- Извлечение двоичных данных
- Двоичные данные и данные больших значений SQL Server
- SQL Server сопоставления типов данных
- Общие сведения об ADO.NET
Как добавить картинку в базу данных sql
Насколько я знаю, непосредственно в базе данных нельзя сохранять картинки, так как они обычно занимают много места и представляют собой бинарные данные. Вместо этого, обычно картинки сохраняются на диске или в облаке, а в базе данных сохраняется только ссылка на файл.
Для сохранения ссылки на файл в базе данных, нужно создать таблицу, которая будет содержать информацию о картинках, например, название, путь к файлу и описание. Для добавления новой картинки в базу данных необходимо выполнить следующие шаги:
- Сохранить картинку на диске или в облаке и получить ее путь или ссылку.
- Открыть соединение с базой данных и выполнить SQL-запрос на вставку новой записи в таблицу картинок. Пример SQL-запроса:
Также можно использовать специальные библиотеки для работы с базами данных, которые могут облегчить процесс вставки картинок в базу данных.
Хранение изображений сайта в БД
Автор должен признаться: статья родилась и выросла из довольно небольшой и не претендующей на откровение оптимизации сайта, описанного в другом материале. Сайт этот связан с музыкальной тематикой и, соответственно, активно отображает обложки альбомов, хранящиеся (до поры до времени) на сервере не в ФС , как обычно бывает, а в БД SQL Server (в BLOB-поле, о чём несложно догадаться); в старом варианте пора извлечения изображений из базы и сохранения их в виде файлов возникает в момент запроса альбомов пользователем, после чего (по истечении сессии) они удаляются.

Чтобы почти окончательно развеять куцую интригу и помочь возможному читателю определиться с тем, имеет ли смысл продолжить чтение, опишу конечный результат оптимизации: обложки по-прежнему останутся в базе данных, причём в поле того же типа, но вот веб-сервер, отдающий в итоге изображения клиенту, будет «обманут» и станет обращаться за файлом знать не зная, что его на самом деле нет в ФС, а вызов идёт сразу и непосредственно к СУБД. Такой «обман» конечно же не является самоцелью – всё в основном затевалось ради уменьшения нагрузки на дисковую подсистему.
Старая реализация
Прежде всего необходимо напомнить структуру HTML-тэга img , обязательным у которого является лишь атрибут src :

У несложных сайтов изображение, находящееся по URL в этом атрибуте, веб-сервер обычно напрямую извлекает из ФС, т. к. относительный путь к нему очень часто совпадает со структурой каталогов на сервере, а имя файла тоже берётся непосредственно из ФС; однако такое распространённое поведение, если брать конкретно IIS (именно он применяется в данном проекте), может быть заменено на совершенно другое за счёт модулей (расширений), когда изображения станут, к примеру, генерироваться на лету (т. е. физически они нигде не хранятся). Так вот статья затрагивает лишь первый вариант.
На этом сайте обложки располагаются не в файловой системе из-за желания «всё своё носить с собой» – чтобы резервная копия БД содержала полный набор информации, была самодостаточной; соответственно, дабы веб-сервер мог отдать изображение клиенту, оно, как уже говорилось, должно быть извлечено из базы данных и сохранено в виде файла, что добавляет лишнюю операцию: сначала читаем из БД, после чего записываем копию на диск, бездарно и теряя дисковое пространство, и нагружая эту подсистему. Ко всему прочему, если несколько разных пользователей относительно одновременно запросят одну и ту же обложку, то из-за того, что в uniGUI каждый из них представлен отдельной сессией, файл дополнительно продублируется ещё и для каждой из них.
Схематически и упрощённо описанное непотребство можно представить следующим образом:

Если проиллюстрировать прошлую реализацию кодом, то его логика будет иметь довольно классический вид:
- Запрос на выборку данных возвращает, среди прочих, и BLOB-поле с обложкой (в данном случае на примере компонента spAlbums типа TFDStoredProc из состава FireDAC).
- Изображение из поля, в зависимости от формата (png, jpg…), загружается в соответствующего наследника TGraphic.
- Далее используется предоставляемая uniGUI функция uniImageToURL , сохраняющая изображение в нужную папку и возвращающая готовый URL для атрибута src .
uses System.SysUtils, System.Classes, Data.DB, Vcl.Graphics, Vcl.Imaging.jpeg, Vcl.Imaging.pngimage, Vcl.Imaging.GIFImg, uniGUIUtils; function TMainForm.AlbumsToHTML: string; function CoverURL: string; var CoverFormat: string; Image: TGraphic; ImageStream: TStream; begin CoverFormat := spAlbums['CoverFormat']; if CoverFormat = 'jpg' then Image := TJPEGImage.Create; if CoverFormat = 'png' then Image := TPngImage.Create; if CoverFormat = 'gif' then Image := TGIFImage.Create; if CoverFormat = 'bmp' then Image := TBitmap.Create; try ImageStream := spAlbums.CreateBlobStream ( spAlbums.FieldByName('Cover'), bmRead ); try Image.LoadFromStream(ImageStream); finally ImageStream.Free; end; Result := uniImageToURL(Image); finally Image.Free; end; end; const AlbumHTMLTemplate = '' + '' + '
' + . '' + ''; begin spAlbums.First; while not spAlbums.Eof do begin Result := Result + Format( AlbumHTMLTemplate, [CoverURL, . ] ); spAlbums.Next; end; end;
Идеальный вариант
Если предаться мечтаниям и представить, что обложки по-прежнему находятся в БД в BLOB-поле, а IIS каким-то образом, почти без усилий и программирования с нашей стороны, сам извлекает их, то окажется, что SQL Server способен такую мечту исполнить – за счёт функционала FileTable, представляющего собой, если очень кратко, создаваемую разработчиком специального вида таблицу, каждая запись которой отображается в файл в NTFS; обращение к таким файлам ничем не отличается от стандартного (однако некоторые возможности не поддерживаются), т. е. их можно просматривать, изменять и удалять хоть через Проводник.
Если снова графически показать такую схему, то мы избавимся от копирования обложки в папку сайта:

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

Чтобы воспользоваться этой манной небесной, ниспосланной SQL Server, потребуется, само-собой, выполнить некоторые действия как на стороне СУБД, так и на стороне IIS, чему собственно и посвящена оставшаяся практическая часть статьи. Но предварительно хотелось бы сделать небольшую ремарку и отметить, что конкуренты Microsoft тоже могут предложить тонкую работу с BLOB, но функционал уровня FileTable автору удалось найти лишь у Oracle – речь о Database File System (DBFS). Остальные же игроки предоставляют лишь условно традиционный (но неизбежно СУБД-специфичный) API, несколько примеров:
- PostgreSQL
- Interbase
- MySQL
Настройка SQL Server
По умолчанию FileTable-возможности отключены в SQL Server. Официальная документация содержит весьма подробную и внятную инструкцию по параметрам, которые необходимо задействовать как на уровне всего экземпляра, так и в конкретной базе данных, отчего не представляется разумным пересказывать здесь подобные тонкости – вместо этого автор сосредоточится на небольшом SQL-скрипте, выполняющем перенос обложек из существующей таблицы с альбомами в новую FileTable-таблицу (если же в Вашем случае файлы изначально в БД не хранятся, в отличие от данного проекта, то с их адаптацией к FileTable поможет документация).
Таблицы
Далее в скрипте предполагается следующая структура двух только что упомянутых таблиц (источника – с данными об альбомах, и приёмника – новой таблицы с обложками):
| Поле | Тип данных | Описание |
|---|---|---|
| ID | integer | Первичный ключ. |
| Cover | varbinary(max) | BLOB-поле с собственно изображением, которое нужно перенести в новую таблицу. |
| CoverFormat | nvarchar(10) | Тип изображения (jpg, png и т. д.). |
| Прочие поля (название альбома, год издания и т. п.). |
| Поле | Тип данных | Описание |
|---|---|---|
| path_locator | hierarchyid | Первичный ключ. |
| file_stream | varbinary(max) | Содержимое файла (в нашем случае это изображение). |
| name | nvarchar(255) | Название файла (с расширением), отображаемое в ФС. |
| Прочие поля. |
| Поле | Тип данных | Описание |
|---|---|---|
| CoverID | hierarchyid | Внешний ключ на приёмник. |
| Прочие поля. |
Скрипт
Все действия с базой логически делятся на несколько этапов:
-
Банальнейшее по реализации добавление поля CoverID в источник:
ALTER TABLE dbo.Album ADD CoverID hierarchyid;
DECLARE @Covers TABLE ( AlbumID integer NOT NULL, path_locator hierarchyid NOT NULL ); MERGE dbo.AlbumCover AS cover USING ( SELECT ID, CoverID, Cover, CAST( NEWID() as nvarchar(max) ) + '.' + CoverFormat AS FileName FROM dbo.Album ) AS album ON album.CoverID = cover.path_locator WHEN NOT MATCHED THEN INSERT (file_stream, name) VALUES (album.Cover, album.FileName) OUTPUT album.ID, INSERTED.path_locator INTO @Covers; UPDATE dbo.Album SET CoverID = path_locator FROM @Covers WHERE > Использование MERGE вместо конструкции INSERT продиктовано невозможностью в последней обратиться в блоке OUTPUT к полям любых задействованных таблиц (допустимы только поля той, в которую и происходит вставка). Другими словами, было бы нечем заполнить поле AlbumID табличной переменной @Covers , т. к. обращение ниже выполняется к таблице-источнику, а вставка идёт в dbo.AlbumCover :
. OUTPUT album.ID, .
ALTER TABLE dbo.Album DROP COLUMN Cover, CoverFormat;
SELECT FileTableRootPath('dbo.AlbumCover');
Оговорка про «почти» связана с тем, что читатель может столкнуться с отсутствием доступа при попытке обратиться к означенным файлам – в этом случае следует ознакомиться с той частью следующего раздела, где описывается нюанс с правами. Также для определённости примем, что наш путь выглядит как \\ServerName\MSSQLSERVER\DatabaseName\AlbumCover (далее в другом примере будет отсылка к нему, где для наглядности потребуется конкретное значение).
Настройка веб-сервера
Хотя в текущем разделе демонстрация идёт на примере IIS, но большинство вещей применимы и к любому другому веб-серверу, ибо являются скорее ОС- и СУБД-специфичными.
Права на чтение
Если снова вернуться к атрибуту src тэга img , то становится очевидной небольшая проблема: путь в атрибуте должен быть относительным, а нужные изображения доступны лишь по UNC-пути. Решение довольно просто́ и заключается в создании символической ссылки в папке сайта; если условиться, что физически сайт располагается в каталоге c:\IIS\SiteName, а ссылка будет называться Covers, то её добавление выполняется следующей командой в консоли:
mklink /d "c:\IIS\SiteName\Covers" "\\ServerName\MSSQLSERVER\DatabaseName\AlbumCover"
Теперь атрибут src станет возможно заполнять подобным образом:

Доработки Delphi-кода, дающие показанное значение для src , будут приведены в конце статьи, а пока же остался важный нюанс с правами (в нашем случае достаточно лишь на чтение), причём делящийся на две части:
-
Предоставление доступа (на уровне ФС) пользователю IUSR и группе IIS_IUSRS для созданной символической ссылки:

-
Логин создаётся на основе Windows-пользователя вида IIS APPPOOL\ .
CREATE LOGIN [IIS APPPOOL\YourPoolName] FROM WINDOWS;
CREATE USER FileTableReader FOR LOGIN [IIS APPPOOL\YourPoolName];
GRANT SELECT ON dbo.AlbumCover TO FileTableReader;
Проверка подлинности
Последнее, что требуется настроить, относится непосредственно к IIS, а именно к проверке подлинности на сайте – чтобы она выполнялась от имени пула приложений, чуть выше только что получившего права на чтение обложек (если в сценарии читателя проверка обязана проходить от иного пользователя, то данный раздел можно пропустить, но при этом необходимо добавить ещё одну пару «логин-пользователь» как описано в предыдущем разделе, связав имя входа со значением из параметров проверки подлинности).

Новая реализация
В заключение осталось привести новый, значительно облегчённый по сравнению с предыдущим вариантом, код, ибо из БД уже не нужно извлекать само изображение – достаточно лишь получить имя файла с ним (из поля dbo.AlbumCover.name , имеющего в примере псевдоним CoverFileName ) и в неизменном виде подставить в атрибут src :
uses System.SysUtils; function TMainForm.AlbumsToHTML: string; const AlbumHTMLTemplate = '' + '' + '
' + . '' + ''; SymbolicLinkName = 'Covers'; begin spAlbums.First; while not spAlbums.Eof do begin Result := Result + Format( AlbumHTMLTemplate, [SymbolicLinkName, spAlbums['CoverFileName'], . ] ); spAlbums.Next; end; end;
- Веб-разработка
- Delphi
- Microsoft SQL Server
- IIS
- Разработка под Windows