Загрузка рисунков в БД
31 марта 2009 года
В процессе работы, достаточно часто возникает ситуация, когда работать приходится не только со стандартными типами данных в базе, но и с изображениями, звуками и другими неструктурированными объектами. При этом можно выделить два основных подхода к хранению подобной информации: 1. В БД хранится только путь к файлу, сам файл хранится на жестком диске. 2. Весь объект хранится в БД в сециальном поле с типом blob
В ряде серверов БД, например Micro$oft SQL 2008 появилась возможность объединить эти подходы, отчасти устранив ряд недостатков первого и второго. Но как обычно у гибридов, решив несколько старых проблем, приобретаем несколько новых. Впрочем речь не об этом.
В этой статье я хотел бы еще раз рассмотреть тему сохранения и извлечения неструктурированной информации на примере сервера БД Firebird и Borland Builder C++ 6. Что бы не усложнять материал, я буду рассматривать работу с рисунками (точнее фотографиями) в формате bmp и jpeg. И затем расскажу как можно расширить приведенную схему.
Итак для работы на нужно создать таблицу, которая содержит идентификатор и связанное с ним изображение. Делает это следующий скрипт:
CREATE TABLE PEOPLE_PHOTOS ( PEOPLE_ID INTEGER NOT NULL, PEOPLE_PHOTO BLOB SUB_TYPE 0 SEGMENT SIZE 80 NOT NULL );
Для операции вставки (модификации) служит хранимая процедура:
CREATE OR ALTER PROCEDURE PEOPLE_PHOTO_INS ( people_id integer, people_photo blob sub_type 0 segment size 80) as declare variable tmp_count integer = 0; begin /* Procedure Text */ select count(*) from people_photos where people_id = :people_id into :tmp_count; if (:tmp_count = 1) then begin update people_photos set people_photo = :people_photo where people_id = :people_id; end else if (:tmp_count = 0) then begin insert into people_photos (people_id, people_photo) values (:people_id, :people_photo); end suspend; end
Для выборки
CREATE OR ALTER PROCEDURE PEOPLE_PHOTO_SEL ( people_id integer) returns ( people_photo blob sub_type 0 segment size 80) as begin /* Procedure Text */ select people_photo from people_photos where people_id = :people_id into :people_photo; suspend; end
Добавлению изображения в базу на стороне клиента служит следующий код:
//Заносим в параметр идентификатор dmMain->fibPhotoIns->ParamByName("people_id")->Value = PeopleId; TMemoryStream *pMS = new TMemoryStream; Graphics::TBitmap *tmpBitmap = new Graphics::TBitmap; TJPEGImage *tmpImg = new TJPEGImage(); try{ //Загружаем изображение из файла и сохраняем его в поток //если это JPEG tmpImg->LoadFromFile(opOpen->FileName); tmpImg->SaveToStream(pMS); }catch(...){ //если нет - то обрабатываем исключение и загружаем битмеп tmpBitmap->LoadFromFile(opOpen->FileName); tmpBitmap->SaveToStream(pMS); } //Загружаем из потока в параметр и вызываем процедуру dmMain->fibPhotoIns->ParamByName("people_photo")->LoadFromStream(pMS); dmMain->fibPhotoIns->ExecProc(); delete pMS; delete tmpBitmap; delete tmpImg;
Соотвественно получение изображения из базы в компонент TImage
imPhoto->Picture->Bitmap->CleanupInstance(); dmMain->fibPhoto->ParamByName("people_id")->Value = tmp; dmMain->fibPhoto->Active = true; TMemoryStream *pMS = (TMemoryStream*)dmMain->fibPhoto->CreateBlobStream (dmMain->fibPhoto->FieldByName("people_photo"),bmRead); if(!pMS->Size){ dmMain->fibPhoto->Active = false; return; } TJPEGImage *tmpImage = new TJPEGImage(); Graphics::TBitmap *tmpBitmap = new Graphics::TBitmap; try{ pMS->Position = 0; tmpImage->LoadFromStream(pMS); imPhoto->Picture->Bitmap->Assign(tmpImage); } catch(...){ pMS->Position = 0; tmpBitmap->LoadFromStream(pMS); imPhoto->Picture->Bitmap->Assign(tmpBitmap); } delete pMS; delete tmpImage; delete tmpBitmap; dmMain->fibPhoto->Active = false;
Обращаю ваше внимание - не забывайте устанавливать позицию потока в 0. Если вы получаете ошибку "JPEG error #52" - значит либо вы забыли это сделать, либо ваш файл не JPEG.
Если в работе вам надо использовать более чем два типа файлов, то генерация исключений будет достаточно накладным решением. В таком случае нужно дополнительно вводить поле типа файла и обрабатывать его, оставляя исключения только для ошибок, либо же пытаться распознавать тип файла по сигнатуре.