UDF, записывающая картинку из файла в BLOB

IBX, FIBPlus, UIB, ADO, .Net и прочее-прочее-прочее, в общем все, что относится к созданию приложений, работающих с InterBase, Firebird и Yaffil - клиент-серверных, трехзвенных, консольных и т.п.

Модератор: kdv

Ответить
Leonid K.
Сообщения: 6
Зарегистрирован: 16 янв 2007, 09:26

UDF, записывающая картинку из файла в BLOB

Сообщение Leonid K. » 16 янв 2007, 11:53

Здравствуйте. Помогите, пожалуйта, разобраться с моей проблемой.

Использую Firebird 2.0+Delphi 6. Есть таблица, в которой хранятся пути к графическим файлам. В клиентском приложении требуется по имени файла получить картинку и вывести её пользователю. Требуется написать UDF, которой передаётся имя файла, она переписывает содержимое файла в переменную типа BLOB и возвращает её. Проблема в том, что мне никак не удаётся реализовать такую функцию.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 16 янв 2007, 12:39

а что, на сайте разве нет?

Leonid K.
Сообщения: 6
Зарегистрирован: 16 янв 2007, 09:26

Сообщение Leonid K. » 16 янв 2007, 13:18

Нашёл только функцию LoadBlobFromFile из UDFDEMO. После её выполнения что-то записывается, но точно не картинка.

Сама функция (немного изменёная):

Код: Выделить всё

function LoadBlobFromFile(FileName: PChar; Blob: PBlob): PBlob; cdecl; export;
const
  MaxBufSize = $8192;
var
  BufSize, ReadLength, StreamSize: Integer;
  Buffer: PChar;
  Stream: TStream;
begin
  Result:=Blob;
  try
    Stream := TFileStream.Create(FileName, fmOpenRead or
                                                 fmShareDenyNone);
    try
      StreamSize := Stream.Size;

      if StreamSize > MaxBufSize then BufSize := MaxBufSize 
                                               else BufSize := StreamSize;
      GetMem(Buffer, BufSize);
      try
        while StreamSize <> 0 do begin
          if StreamSize > BufSize then ReadLength := BufSize 
                                             else ReadLength := StreamSize;
          Stream.ReadBuffer(Buffer^, ReadLength);

          Blob^.PutSegment(Blob^.BlobHandle, Buffer, ReadLength);

          Dec(StreamSize, ReadLength);
        end;
      finally
        FreeMem(Buffer, BufSize);
      end;
    finally
      Stream.Free;
    end;
  except
    {$Ifdef Debug}
      on E: Exception do begin
        Writeln(X, 'Exception in LoadBLObFromFile!!!');
        Writeln(X, 'FileName ', FileName);
        Writeln(X, E.Message);
        Flush(X);
      end;
    {$Endif}
  end;
end;
Объявление её в базе:

Код: Выделить всё

DECLARE EXTERNAL FUNCTION GET_IMAGE
    CSTRING(100),
    BLOB,
    BLOB
RETURNS PARAMETER 3
ENTRY_POINT 'LoadBlobFromFile' MODULE_NAME 'FreeUDFLib'
Вызов:

Код: Выделить всё

CREATE PROCEDURE "GetImage" (
    id integer)
returns (
    res integer)
as
declare variable imgid integer;
declare variable fname varchar(100);
declare variable path varchar(70);
declare variable img blob sub_type 0 segment size 16384;
BEGIN
    res=0;
    
    --получение пути к картинке
    SELECT "ImageId" FROM "Object" WHERE "Id1"=:id INTO :ImgId;
    select "ImageFile" FROM "Images" WHERE "Id1"=:ImgId INTO :fname;
    SELECT "Value" FROM "Options" WHERE "Param"='ImagePath' INTO :path;

    fname=:path||:fname;

    --получение самой картинки
    img=GET_IMAGE(:fname,:img);

    INSERT INTO "TempObject" ("Image") VALUES (:img);
END

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 16 янв 2007, 14:13

а если сохранить из блоба в файл - будет картинка?

кстати:

Код: Выделить всё

declare variable img blob sub_type 0 segment size 16384; 
вот это безобразие в виде segment size - зачем?

Leonid K.
Сообщения: 6
Зарегистрирован: 16 янв 2007, 09:26

Сообщение Leonid K. » 16 янв 2007, 15:00

При сохранении BLOB в файл получается пустой рисунок.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 16 янв 2007, 16:19

Leonid Kul писал(а):Нашёл только функцию LoadBlobFromFile из UDFDEMO....
Сама функция (немного изменёная):
Фиг поверю, что немного. Найди отличия

Код: Выделить всё

function LoadBlobFromFile(FileName: PChar; Blob: PBlob): PBlob;
procedure LoadBLObFromFile(FileName: PChar; Blob: PBlob);
И зачем ещё один BLOb в объявлении функции?
Не пробовал и пробовать не стану, будет ли вот так работать правильно:

Код: Выделить всё

img=GET_IMAGE(:fname,:img);
Вот скопировал бы как в демо, и объявил без выкрутасов - и всё б заработало.

Leonid K.
Сообщения: 6
Зарегистрирован: 16 янв 2007, 09:26

Сообщение Leonid K. » 16 янв 2007, 17:25

При объявлении функции, как

Код: Выделить всё

procedure LoadBLObFromFile(FileName: PChar; Blob: PBlob);
и следующем её вызове:

Код: Выделить всё

GET_IMAGE(:fname,:img);
img равняется NULL (имя файла передаётся верно).

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 16 янв 2007, 17:32

Да понятно, понятно.
Только это совсем нетрадиционный подход.
Говорю ж - нафиг доп.параметр, который и не нужен? Функция проверено работает в том виде, как в демке. Если тебе нужна именно такая, чего эксперементируешь-то?

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

Leonid K.
Сообщения: 6
Зарегистрирован: 16 янв 2007, 09:26

Сообщение Leonid K. » 16 янв 2007, 17:46

Я же написал выше, что в том виде как в демке тоже не работает. В этом случае в таблицу записывается null.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 16 янв 2007, 18:05

Leonid K. писал(а):Я же написал выше, что в том виде как в демке тоже не работает. В этом случае в таблицу записывается null.
Вернись к родному варианту процедуры, и выполни в IBExpert'е следующее:

Код: Выделить всё

select GET_IMAGE ('тут путь с именем файла') from rdb$database
Эксперт умеет показывать картинки в том числе, да и в бинарном виде будет сразу видно.
А то может у тебя для сервера доступа к каталогу с картинками нет, а ты тут нам про неработу UDF рассказываешь.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 16 янв 2007, 18:57

а у меня - работает. я не скажу чего делаю, но функция LoadBlobFromFile, выдранная 1 в 1 из udfdemo, прекрасно загружает файл в блоб (на fb 1.5. на 2.0 тоже проверю).

Leonid K.
Сообщения: 6
Зарегистрирован: 16 янв 2007, 09:26

Сообщение Leonid K. » 16 янв 2007, 19:25

Всё, разобрался. Всем спасибо.
Всё, как обычно, из-за моей невнимательности. Перечитав ещё раз "Как научиться писать UDF за 21 минуту", понял свою ошибку. LoadBlobFromFile из UDFDEMO работает нормально, просто меня смутило то, что это процедура, а не функция, и я неправильно её задекларировал в Firebird.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 16 янв 2007, 19:49

и я неправильно её задекларировал в Firebird.
вот-вот. в этом плане на сайте все проверено, ошибок нет.
также стоило посмотреть www.ibase.ru/download/safeudf.zip

p.s. собственно, я решил сделать отдельную либу с save и load блобов.
и все вроде бы нормально, только при сохранении буфера в файл почему-то у меня лезет ошибка записи в поток. вот дела...

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 16 янв 2007, 20:37

kdv писал(а):только при сохранении буфера в файл почему-то у меня лезет ошибка записи в поток. вот дела...
Интересно.
У меня тоже была такая срань - я правда не в файл, а просто в TMemoryStream буфер пытался запихать - скрючивало.
Так и не отладил - необходимость в процедуре отпала, и руки не дошли с тех пор.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 16 янв 2007, 21:56

вот-вот. и главное, в firebird.conf установлен полный доступ к external files. странные дела творятся...

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 17 янв 2007, 15:08

www.ibase.ru/download/blobsaveload.zip

декларация, исходник (Delphi) и dll (D2006).

Функции LoadBLOBFromFile и SaveBLOBToFile.

SaveBLOBToFile в качестве результата выдает
размер сохраненного блоба.

в blobsaveload.sql - декларация функций и пример использования.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 17 янв 2007, 15:58

О как. Кому верить?
BLObSaveLoad.dpr писал(а):EndOfBLOb := GetSegment(Handle, Buffer, MaxBufSize, GotLength);
DEMO/udflib.pas писал(а):EndOfBLOb := not GetSegment(Handle, Buf + ReadLen, FreeBufLenX, GotLength);

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 17 янв 2007, 16:20

когда Кукарцев писал на дельфях, дельфя еще позволяла над числовыми типами выполнять булевские операции.
а я не стал морочить голову, применяя wordbool.

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

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 17 янв 2007, 17:29

Тьфу, не обратил внимание на until EndOfBLOb = 0;
Привык как-то наоборот, на TRUE сравнивать :)

Ответить