Запись данных в BLOB
В качестве примера записи BLOB рассмотрим функцию читающую содержимоеBLOB из файла.
Note
|
Замечание
Этот пример является адаптированной версией UDF функций для чтения изаписи BLOB из/в файл. Оригинальная UDF доступна по адресуblobsaveload.zip |
Утилиты для чтения и записи BLOB из/в файл оформлены в виде пакета
CREATE PACKAGE BlobFileUtils
AS
BEGIN
PROCEDURE SaveBlobToFile(ABlob BLOB, AFileName VARCHAR(255) CHARACTER SET UTF8);
FUNCTION LoadBlobFromFile(AFileName VARCHAR(255) CHARACTER SET UTF8) RETURNS BLOB;
END^
CREATE PACKAGE BODY BlobFileUtils
AS
BEGIN
PROCEDURE SaveBlobToFile(ABlob BLOB, AFileName VARCHAR(255) CHARACTER SET UTF8)
EXTERNAL NAME 'BlobFileUtils!SaveBlobToFile'
ENGINE UDR;
FUNCTION LoadBlobFromFile(AFileName VARCHAR(255) CHARACTER SET UTF8) RETURNS BLOB
EXTERNAL NAME 'BlobFileUtils!LoadBlobFromFile'
ENGINE UDR;
END^
Зарегистрируем фабрики наших процедур и функций:
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr;
AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl;
begin
// регистрируем
AUdrPlugin.registerProcedure(AStatus, 'SaveBlobToFile', TSaveBlobToFileProcFactory.Create());
AUdrPlugin.registerFunction(AStatus, 'LoadBlobFromFile', TLoadBlobFromFileFuncFactory.Create());
theirUnloadFlag := AUnloadFlagLocal;
Result := @myUnloadFlag;
end;
В данном случае приведём пример только для функции считывающий BLOB изфайла, полный пример UDR вы можете скачать по адресу06.BlobSaveLoad.Интерфейсная часть модуля с описанием функции LoadBlobFromFile
выглядитследующим образом:
interface
uses
Firebird, Classes, SysUtils;
type
// входное сообщений функции
TInput = record
filename: record
len: Smallint;
str: array [0 .. 1019] of AnsiChar;
end;
filenameNull: WordBool;
end;
TInputPtr = ^TInput;
// выходное сообщение функции
TOutput = record
blobData: ISC_QUAD;
blobDataNull: WordBool;
end;
TOutputPtr = ^TOutput;
// реализация функции LoadBlobFromFile
TLoadBlobFromFileFunc = class(IExternalFunctionImpl)
public
procedure dispose(); override;
procedure getCharSet(AStatus: IStatus; AContext: IExternalContext;
AName: PAnsiChar; ANameSize: Cardinal); override;
procedure execute(AStatus: IStatus; AContext: IExternalContext;
AInMsg: Pointer; AOutMsg: Pointer); override;
end;
// Фабрика для создания экземпляра внешней функции LoadBlobFromFile
TLoadBlobFromFileFuncFactory = class(IUdrFunctionFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AInBuilder: IMetadataBuilder;
AOutBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalFunction; override;
end;
Приведём только реализацию основного метода execute классаTLoadBlobFromFile
, остальные методы классов элементарны.
procedure TLoadBlobFromFileFunc.execute(AStatus: IStatus;
AContext: IExternalContext; AInMsg: Pointer; AOutMsg: Pointer);
const
MaxBufSize = 16384;
var
xInput: TInputPtr;
xOutput: TOutputPtr;
xFileName: string;
xStream: TFileStream;
att: IAttachment;
trx: ITransaction;
blob: IBlob;
buffer: array [0 .. 32767] of Byte;
xStreamSize: Integer;
xBufferSize: Integer;
xReadLength: Integer;
begin
xInput := AInMsg;
xOutput := AOutMsg;
if xInput.filenameNull then
begin
xOutput.blobDataNull := True;
Exit;
end;
xOutput.blobDataNull := False;
// получаем имя файла
xFileName := TEncoding.UTF8.GetString(TBytes(@xInput.filename.str), 0,
xInput.filename.len * 4);
SetLength(xFileName, xInput.filename.len);
// читаем файл в поток
xStream := TFileStream.Create(xFileName, fmOpenRead or fmShareDenyNone);
att := AContext.getAttachment(AStatus);
trx := AContext.getTransaction(AStatus);
blob := nil;
try
xStreamSize := xStream.Size;
// определяем максимальный размер буфера (сегмента)
if xStreamSize > MaxBufSize then
xBufferSize := MaxBufSize
else
xBufferSize := xStreamSize;
// создаём новый blob
blob := att.createBlob(AStatus, trx, @xOutput.blobData, 0, nil);
// читаем содержимое потока и пишем его в BLOB посегментно
while xStreamSize <> 0 do
begin
if xStreamSize > xBufferSize then
xReadLength := xBufferSize
else
xReadLength := xStreamSize;
xStream.ReadBuffer(buffer, xReadLength);
blob.putSegment(AStatus, xReadLength, @buffer[0]);
Dec(xStreamSize, xReadLength);
end;
// закрываем BLOB
// метод close в случае успеха совобождает интерфейс IBlob
// поэтому последующий вызов release не нужен
blob.close(AStatus);
blob := nil;
finally
if Assigned(blob) then
blob.release;
trx.release;
att.release;
xStream.Free;
end;
end;
Первым делом необходимо создать новый BLOB и привязать его в blobIdвыхода с помощью метода createBlob
интерфейса IAttachment
. Поскольку мыпишем пусть и временный BLOB для своей базы данных, то будем создаватьего в контексте текущего подключения. Контекст текущего подключения иконтекст текущей транзакции мы можем получить из контекста внешнейпроцедуры, функции или триггера (интерфейс IExternalContext
).
Так же как и в случае с чтением данных из BLOB, запись ведётся посегментно с помощью метода putSegment
интерфейса IBlob
до тех пор, покаданные в потоке файла не закончатся. По завершению записи данных в BLOBнеобходимо закрыть его с помощью метода close
.
Important
|
Важно
Метод |