Title: Need help with ByteStream assignment Post by: kavetu 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 Title: Re: Need help with ByteStream assignment Post by: D.Tkalcec (RTC) 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 Title: Re: Need help with ByteStream assignment Post by: D.Tkalcec (RTC) 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 Title: Re: Need help with ByteStream assignment Post by: kavetu 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 Title: Re: Need help with ByteStream assignment Post by: D.Tkalcec (RTC) 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 Title: Re: Need help with ByteStream assignment Post by: kavetu 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 Title: Re: Need help with ByteStream assignment Post by: D.Tkalcec (RTC) 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 Title: Re: Need help with ByteStream assignment Post by: kavetu 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 |