суббота, 4 февраля 2012 г.

Полезняшки. Загрузка файла в БД в виде внедренного OLE-объекта без использование стандартного OleContainer

Добрый день!
Недавно встала задача загружать файлы документов в БД (поле BLOB) в виде OLE-объекта, которого в дальнейшем можно вывести в стандартный OLE контейнер Delphi для просмотра и последующего редактирования.

Первое что встает в голову воспользоватся стандартной функций OleContainer - CreateObjectFromFile.

т.е. сделать что то вроде такого кода

Table.Append;
try
Stream := TMemoryStream.Create;
OleContainer.CreateObjectFromFile(OpenDialog.FileName);
OleContainer.SaveToStream(Stream);
TableDATA.LoadFromStream(Stream);
finally
 Stream.Free;
end;
Table.Post;

где  TableData - это Blob-поле. Всем хорош данный код, но мне он не понравился по 2-м причинам:
1.нужно иметь OleContainer на форме
2.загрузка таким способом происходит довольно медленно, т.к. если OleContainer находится на форме, много времени тратится на лишнии операции (прорисовка и пр)
поэтому хотелось бы загружать файл документа сразу в БД минуя OleContainer, т.е создать внедренный Ole-объект на основе файла документа и поместить его в БД.

Вообщем повозившись полчасика родилось такое решение, которое по производительности обошло загрузку стандартным способом:
const
 StreamSignature = $434F4442; {'BDOC'}
type
 TStreamHeader = record
case Integer of
  0: ( { New }
  Signature: Integer;
  DrawAspect: Integer;
  DataSize: Integer);
  1: ( { Old }
   PartRect: TSmallRect);
end;
var
 ID : Integer;
 fDocOLE : TfDocOLE;
 FN : string;
 Stream : TMemoryStream;
 FStorage : IStorage; {Интерфейс хранилища}
 FOleObject: IOleObject; {Интерфейс является основным средством, с помощью которого
                          внедренный объект наделяется базовой функциональностью,
                          и взаимодействует с контейнером хранилища}
 FLockBytes: ILockBytes; {Интерфейс структурированного хранилища, который
                          служит для доступа к длинным текстовым и
                          двоичным данным}
 DataHandle: HGlobal;
 Buffer: Pointer;
 Header: TStreamHeader;
begin
if OpenDialog.Execute then
begin
 FN := ExtractFileName(OpenDialog.FileName);
 FOleObject := nil;
 FStorage := nil;
 FLockBytes := nil;

 { Доступ к памяти для реализации ILockBytes }
 OleCheck(CreateILockBytesOnHGlobal(0, True, FLockBytes));
 { Создание хранилища IStorage и связка его с реализацией интерфеса
 ILockBytes }
 OleCheck(StgCreateDocfileOnILockBytes(FLockBytes,
          STGM_READWRITE or STGM_SHARE_EXCLUSIVE or STGM_CREATE, 0, FStorage));
 // Создание внедеренного объекта из файла
 OleCheck(OleCreateFromFile(GUID_NULL, PWideChar(OpenDialog.FileName),
 IOleObject, OLERENDER_NONE, nil, nil, FStorage, FOleObject));
 OleCheck(GetHGlobalFromILockBytes(FLockBytes, DataHandle));

 try
 { Создаем поток в памяти }
 Stream := TMemoryStream.Create;
 Stream.Position := 0;
 Header.Signature := StreamSignature;
 Header.DrawAspect := DVASPECT_CONTENT; { Контент }
 Header.DataSize := GlobalSize(DataHandle);
 // Записываем заголовок + контент
 Stream.WriteBuffer(Header, SizeOf(Header));
 try
  Buffer := GlobalLock(DataHandle);
  // Записываем размер заголовка
  Stream.WriteBuffer(Buffer^, Header.DataSize);
 finally
  GlobalUnlock(DataHandle);
 end;

 Table.Append;
 Table NAME.Value := FN;
 Table DATA.LoadFromStream(Stream);
 Table.Post;
finally
 Stream.Free;
end;
end; { OpenDialog }



Комментариев нет:

Отправить комментарий