RTC Forums
May 05, 2024, 07:36:25 PM *
Welcome, Guest. Please login or register.

Login with username, password and session length
 
   Home   Help Login Register  
Pages: [1]
  Print  
Author Topic: Need help with ByteStream assignment  (Read 5575 times)
kavetu
Newbie
*
Posts: 20


« on: May 11, 2010, 12:33:39 PM »

 :'(I need urgent help with assigning a blob field value (PDF image
stored in an Oracle BLOB field) to an RtcDataSet field. This is the procedure

procedure TdmoFunctions.RtcDataSetFromDocumentImage(DocImage:TOraQuery; rtcDS: TRtcDataSet);
begin
  rtcDS.Clear;

  rtcDS.SetField('DOCUMENTS_BLOB_ID',ft_Integer,0,True);
  rtcDS.SetField('DOCUMENTS_ID',ft_Integer,0,True);
  rtcDS.SetField('DOC_IMAGE',ft_Blob,0,True);
  rtcDS.SetField('MIGRATION',ft_Integer,0,True);
  rtcDS.SetField('DOC_REF_NO',ft_String,50,True);
  rtcDS.SetField('BARCODE',ft_String,50,True);

  DocImage.First;

  rtcDS.Append;

  rtcDS.FieldByName('DOCUMENTS_BLOB_ID').asInteger := 
  DocImage.FieldByName('DOCUMENTS_BLOB_ID').AsInteger;
  rtcDS.asInteger['DOCUMENTS_ID'] := DocImage.FieldByName('DOCUMENTS_ID').AsInteger;
  rtcDS.asByteStream['DOC_IMAGE'] := DocImage.FieldByName('DOC_IMAGE').AsBytes; //i know this is wrong and this is where i have the problem
  rtcDS.asInteger['MIGRATION'] := DocImage.FieldByName('MIGRATION').AsInteger;
  rtcDS.asString['DOC_REF_NO'] := DocImage.FieldByName('DOC_REF_NO').AsString;
  rtcDS.asString['BARCODE'] := DocImage.FieldByName('BARCODE').AsString;
end;

Basically I would like to assign
DocImage.FieldByName('DOC_IMAGE').AsBytes to
rtcDS.asByteStream['DOC_IMAGE'] and I am not sure how to do this. Is there
a way to cast DocImage.FieldByName('DOC_IMAGE').AsBytes to a TStream object
or should I create a TStream object and copy DocImage.FieldByName('DOC_IMAGE').AsBytes
into it (and how do i do this). In the past i had problems working with TStream and TMemoryStream
objects. I need urgent help with this please.

Manfredt Kavetu
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #1 on: May 11, 2010, 12:53:30 PM »

Hi Manfredt,

unless I am mistaken, all Delphi TDataset components should have a "CreateBlobStream" method, which you should use to create a "TBlobStream", which you can then use to copy the contents of the Blob into another stream, after which you simply destroy the "TBlobStream". I'm not sure the code below will compile, but I think the solution to your problem looks something like ...

procedure TdmoFunctions.RtcDataSetFromDocumentImage(DocImage:TOraQuery; rtcDS: TRtcDataSet);
var
  tmp:TStream;
begin
  rtcDS.SetField('DOCUMENTS_BLOB_ID',ft_Integer,0,True);
  rtcDS.SetField('DOCUMENTS_ID',ft_Integer,0,True);
  rtcDS.SetField('DOC_IMAGE',ft_Blob,0,True);
  rtcDS.SetField('MIGRATION',ft_Integer,0,True);
  rtcDS.SetField('DOC_REF_NO',ft_String,50,True);
  rtcDS.SetField('BARCODE',ft_String,50,True);

  rtcDS.Append;

  rtcDS.asInteger['DOCUMENTS_BLOB_ID'] := DocImage.FieldByName('DOCUMENTS_BLOB_ID').AsInteger;
  rtcDS.asInteger['DOCUMENTS_ID'] := DocImage.FieldByName('DOCUMENTS_ID').AsInteger;
  rtcDS.asString['DOC_REF_NO'] := DocImage.FieldByName('DOC_REF_NO').AsString;
  rtcDS.asString['BARCODE'] := DocImage.FieldByName('BARCODE').AsString;
  rtcDS.asInteger['MIGRATION'] := DocImage.FieldByName('MIGRATION').AsInteger;

  tmp:=DocImage.CreateBlobStream(DocImage.FieldByName('DOC_IMAGE'),bmRead);
  try
    rtcDS.newByteStream('DOC_IMAGE').copyFrom(tmp,0);
  finally
    tmp.Free;
    end;
end;

Best Regards,
Danijel Tkalcec
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #2 on: May 11, 2010, 01:25:00 PM »

Btw ... on the receiver side, you can probably copy the contents of the stream from rtcByteStream to a TBlobStream by using something like ...

tmp:=DocImage.CreateBlobStream(DocImage.FieldByName('DOC_IMAGE'),bmWrite);
try
  tmp.CopyFrom(rtcDS.asByteStream['DOC_IMAGE'],0);
finally
  tmp.Free;
  end;

Best Regards,
Danijel Tkalcec
Logged
kavetu
Newbie
*
Posts: 20


« Reply #3 on: May 11, 2010, 02:38:56 PM »

Thanks Danijel, your code works perfectly. My client is very thin as I do not have
oracle data access components on the client (they are only on the server). On the
client I am using kbmMemTable (i had unresolved issue with TClientDataset). My OnResult
procedure on the client is like this:

procedure TfrmMain.RtcDocumentImageReturn(Sender: TRtcConnection; Data,
  Result: TRtcValue);
var
  msg: string;
begin
  msg := dmoGlobal.ProcessRtcResultExceptions(Result);
  if msg = 'ds' then
  begin
    if not Result.asDataSet.Empty then
    begin
      Result.asDataSet.First;
      with dmoDocument do
      begin
        if not kbmDocumentImage.Active then
          kbmDocumentImage.Open;

        kbmDocumentImage.EmptyTable;

        kbmDocumentImage.Append;

        kbmDocumentImageDOC_IMAGE.LoadFromStream(Result.asDataSet.FieldByName('DOC_IMAGE').asByteStream);

        kbmDocumentImage.FieldByName('DOCUMENTS_BLOB_ID').asInteger :=
        Result.asDataSet.FieldByName('DOCUMENTS_BLOB_ID').asInteger;

        kbmDocumentImage.FieldByName('DOCUMENTS_ID').AsInteger :=
        Result.asDataSet.FieldByName('DOCUMENTS_ID').asInteger;

        kbmDocumentImage.FieldByName('MIGRATION').AsInteger :=
        Result.asDataSet.FieldByName('MIGRATION').asInteger;

        kbmDocumentImage.FieldByName('DOC_REF_NO').AsString :=
        Result.asDataSet.FieldByName('DOC_REF_NO').AsString;

        kbmDocumentImage.FieldByName('BARCODE').AsString :=
        Result.asDataSet.FieldByName('BARCODE').AsString;

        kbmDocumentImage.Post;

        StatusBar.Panels[0].Text := 'SUCCESS'
      end;
    end
    else
      StatusBar.Panels[0].Text := 'Rtc dataset from server is empty';

    open := True;
  end
  else
    StatusBar.Panels[0].Text := msg;
end;

Thus I am able to load my bytestream (pdf file in an Oracle Blob) in a Blob field of
a kbmMemTable dataset with this statement:

kbmDocumentImageDOC_IMAGE.LoadFromStream(Result.asDataSet.FieldByName('DOC_IMAGE').asByteStream);

And it works superbly. Everything is fast. Retrieving the PDF from Oracle server (via a
RTC Webserver) to the client is almost instantaneous, and i can save the pdf file in the kbmMemTable
with this line:

dmoDocument.kbmDocumentImageDOC_IMAGE.SaveToFile('dwas.pdf');

I have installed the PDF ActiveX component into Delphi and can load the PDF file on the
local hard drive into the ActiveX PDF object with this line (AcroPDF is the ActiveX PDF object in delphi):

AcroPDF.LoadFile('dwas.pdf');

IS THERE A WAY THOUGH OF FEEDING THE PDF ACTIVEX OBJECT DIRECTLY WITH THE
PDF FILE FROM THE STREAM (STORED IN KBMMEMTABLE) WITHOUT FIRST DOWNLOADING
THE PDF FILE TO THE LOCAL FOLDER?

Thanks again very very much.

Manfredt Kavetu
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #4 on: May 11, 2010, 02:52:14 PM »

I'm afraid I can't help you with the ActiveX PDF component. The component you are using needs methods for loading from a stream instead of a file. If there is such a method, you can use it to feed the stream directly from "asByteStream" of the rtcDataSet object.

As a work-around, in case the PDF components you are using do NOT know how to work with streams, you could write the PDF file into a temporary file inside the Windows Temp folder and delete the file directly after that. The "rtcInfo.pas" unit also has a "GetTempFile" function, which you can use to get the next available (unused) file name inside the Windows temp folder. Something like ...

{ need "uses rtcInfo;" inside the unit where this code is }

uses ..., rtcInfo;
...
var myTempFile:String;
...
myTempFile:=GetTempFile;
dmoDocument.kbmDocumentImageDOC_IMAGE.SaveToFile(mytempFile);
AcroPDF.LoadFile(myTempFile);
Delete_File(myTempFile);

Best Regards,
Danijel Tkalcec
Logged
kavetu
Newbie
*
Posts: 20


« Reply #5 on: May 11, 2010, 03:15:29 PM »

I have a problem accessing some user interface objects from within
the OnResult (procedure TdmoFunctions.RtcDataSetFromDocumentImage(DocImage:TOraQuery; rtcDS: TRtcDataSet) handle on my client.

On the client I have a PageControl with four tabs and on one of the tabs I have
a TAcroPDF control. Even this line from within the RtcDataSetFromDocumentImage
procedure freezes my client:

PageControl1.ActivePageIndex := 2;

I was too quick to report that things work correctly but it seems I cannot interact with
some user interface components from within the
TdmoFunctions.RtcDataSetFromDocumentImage procedure.

Do i need to implement some sort of syncronisation mechanism in order
to interact with some User interface elements from within the OnResult
handler?

Manfredt Kavetu

Manfredt Kavetu

Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #6 on: May 11, 2010, 03:20:35 PM »

If you are using TRtcHttpClient components with MultiThreaded=TRUE, you can set "AutoSyncEvents:=TRUE" for the TRtcClientModule component to have all events fired from that component synchronized with the main thread. This also includes OnResult events from TRtcResult components used to receive results from remote functions sent over the TRtcClientModule. Or ... if you do NOT want all the events synchronized with the MainThread, you can manually synchronize the events by using the Sync() method inside the OnResult event:

procedure TfrmMain.RtcDocumentImageReturn(Sender: TRtcConnection; Data, Result: TRtcValue);
begin
  if not Sender.inMainThread then
    Sender.Sync(RtcDocumentImageReturn, Data, Result) // -> THIS will call the event synchronized!
  else
    begin
    ... here comes your normal event code ...
    end;
end;

Best Regards,
Danijel Tkalcec
Logged
kavetu
Newbie
*
Posts: 20


« Reply #7 on: May 11, 2010, 03:30:20 PM »

Aha, I set AutoSynchEvents on my TRtcClientModule to True and everything works 100% and perfect,
thanks alot Danijel. This also solved some of my earlier funny things like sometimes displaying a message
"cannot draw on the canvas". EVERYTHING IS PERFECT NOW, THANKS AGAIN VERY MUCH.

Manfredt Kavetu
Logged
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Valid XHTML 1.0! Valid CSS!
Page created in 0.026 seconds with 16 queries.