RTC Forums
April 29, 2024, 05:09:46 PM *
Welcome, Guest. Please login or register.

Login with username, password and session length
 
   Home   Help Login Register  
Pages: [1] 2
  Print  
Author Topic: Blob  (Read 9066 times)
cdhaene
RTC Expired
*
Posts: 13


« on: September 09, 2017, 08:05:14 AM »

Hi,

I'm trying to make an uploadfunction using RTC.
Aim is to upload sqlite-db from a smartphone to my server.
Download works fine, but how to pass a blob parameter to my function?

So far, I'm having this, but it doesn't work.

Code:
with RtcClientModule1 do
  begin
    StartCalls;
    try
      with Data.NewFunction('f_initRTCClientDataset') do
      begin
        asLargeInt['clientdataset'] := nativeUint(aClientDataset);
        asString['datamod'] := aDataModuleName;
        asString['query'] := aQueryName;
 
        if aFixedSQL <> '' then
          asString['fixedsql'] := aFixedSQL
        else
          asString['sql'] := asql;
 
        lParams := NewArray('Params');
 
        NewByteStream('stream');
        asByteStream['stream'].CopyFrom(aStream, aStream.Size);
        asByteStream['stream'].Seek(0, soFromBeginning);
        lParams.asValue[0] := asByteStream['stream'];
 
        if length(arParams) > 0 then
        begin
          for i:= low(arParams) to high(arParams) do
          begin
            lParams.asValue[i+1] := arParams[i];
          end;
        end;
 
        asBoolean['open'] := false;
        asBoolean['execute'] := true;
        astream.SaveToFile(ModeSelectionForm.PathToInternalStorage + 'Database/DATATEST.DB');
      end;
      call(r_initRTCClientDataset);
    finally
      post;
 
      waitforCompletion;
    end;
  end;

The blob is the problem.

Can you help me?

Many thanks!

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


« Reply #1 on: September 09, 2017, 10:21:18 AM »

I see from your code that you are using CopyFrom() two times on "aStream". Once to copy "aStream" into asByteStream['stream'] and then again using the same Stream with SaveToFile. Is your "aStream" at position 0 before using CopyFrom() for the 1st time and are you moving "aStream" back to position 0 again before reading from "aStream" again for the 2nd time?

If that doesn't solve your problem, then please post your code used on the Client to populate the Stream, as well as the code you are using on the Server to read the Stream, since the problem could be on either side.

Best Regards,
Danijel Tkalcec
Logged
cdhaene
RTC Expired
*
Posts: 13


« Reply #2 on: September 10, 2017, 02:32:24 PM »

Danijel,

still no result...

At client-side, on the smartphone I have this code now:

Code:
procedure TRTCControl.RTCUpload(aClientDataset : TClientDataSet; aDataModuleName,
                                aQueryName, aSql: string;
                                arParams : array of variant;
                                aStream : TMemoryStream ;
                                aFixedSQL : string='';
                                aOpenDataset : boolean = false;
                                aExecuteDataSet : boolean = False);
var
    i : integer;
    lParams : TrtcArray;
begin
  aClientDataSet.Close;

  aClientDataset.RemoteServer := nil;
  aClientDataset.ProviderName := '';
  with RtcClientModule1 do
  begin
    StartCalls;
    try
      with Data.NewFunction('f_initRTCClientDataset') do
      begin
        asLargeInt['clientdataset'] := nativeUint(aClientDataset);
        asString['datamod'] := aDataModuleName;
        asString['query'] := aQueryName;

        if aFixedSQL <> '' then
          asString['fixedsql'] := aFixedSQL
        else
          asString['sql'] := asql;

        lParams := NewArray('Params');
        if length(arParams) > 0 then
        begin
          for i:= low(arParams) to high(arParams) do
          begin
            lParams.asValue[i] := arParams[i];
          end;

        end;
        NewByteStream('stream');
        aStream.Seek(0, soFromBeginning);
        asByteStream['stream'].CopyFrom(aStream, aStream.Size);
        asByteStream['stream'].Seek(0, soFromBeginning);
        asBoolean['open'] := false;
        asBoolean['execute'] := true;
        aStream.Seek(0, soFromBeginning);
        astream.SaveToFile(ModeSelectionForm.PathToInternalStorage + 'Database/DATATEST.DB');
      end;
      call(r_initRTCClientDataset);
    finally
      post;

      waitforCompletion;
    end;
  end;
end;

At server side, this code :

Code:
procedure TServerContainer.f_initRTCClientDatasetExecute(Sender: TRtcConnection;
  Param: TRtcFunctionInfo; Result: TRtcValue);
var
  rtcDataset : TrtcDataset;
  arParams : array of variant;
  i : integer;
  myQuery : TUniQuery;

  // start local function assignMyQuery
  // Search the TuniQuery-component in all datamodules of the application
  function assignMyQuery(aDatamod, aQuery : string) : TUniQuery;
  var
    i : integer;
    j : integer;
  begin
    Result := nil;
    for i:= 0 to application.ComponentCount-1 do
    begin
      if application.Components[i] is TDataModule then
      begin
        if lowercase(TDataModule(application.Components[i]).Name) = lowercase(aDataMod) then
        begin
          for j:= 0 to application.Components[i].ComponentCount-1 do
            if application.Components[i].Components[j] is TUniQuery then
            begin
              if lowercase(TUniQuery(application.Components[i].Components[j]).Name) = lowercase(aQuery) then
                exit(TUniQuery(Application.Components[i].Components[j]));
            end;
        end;
      end;
    end;
  end;
  //   end local function assignMyQuery

  //
  function GetQuery(AQuery: String): string;
  begin
    with qGetQuery do
    begin
      Close;
      Params[0].AsString := AQuery;
      Open;
      Result := FieldByName('SQL').AsString;
    end;
  end;


begin
  try
    myQuery := assignmyQuery(Param.asText['datamod'], Param.asText['query']);
    if myQuery = nil then
    begin
      dbLog('ERROR',format('Error ! : The query %s on datamodule %s doesnot exist !',[Param.asText['query'], Param.asText['datamod']]));
      addLineToLog(format('Error ! : The query %s on datamodule %s doesnot exist !',[Param.asText['query'], Param.asText['datamod']]));
      Result.NewException;
    end
    else
    begin
      myQuery.close;
      if Param.asText['fixedsql'] <> '' then
      begin
        myQuery.sql.Text := Param.asText['fixedsql'];
      end
      else if Param.asText['sql'] <> '' then
      begin
        myQuery.sql.Text := getQuery(Param.asText['sql']);
      end;

      if (Param.asArray['params'].Count) > 0 then
        for i:= 0 to Param.asArray['params'].Count-1 do
        begin
          myQuery.params[i].value := Param.asArray['params'][i];
        end;

      if Param.asBoolean['open'] = True then
      begin
        myQuery.active := True;
        DelphiDataSetToRtc(myQuery, result.NewDataSet);
      end
      else if Param.asBoolean['execute'] = True then
      begin
        myQuery.execute;
        Result.asBoolean := True;
      end;
    end;
  except
    on E: Exception do
    begin
      dbLog('ERROR', format('exception ! %s %s',[E.ClassName, E.Message]));
      AddLineToLog(format('exception ! %s %s',[E.ClassName, E.Message]));
      result.asException := 'exception !';
    end;
  end;
end;
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #3 on: September 10, 2017, 03:40:26 PM »

Since you've stored the contents from your "aStream" in the 'stream' parameter of your remote function call object (using NewByteStream('stream') and then asByteStream['stream'] on the TRtcFunctionInfo object returned from Data.NewFunction...), you should be using Param.asByteStream['stream'] somewhere from the OnExecute event on the Server to access the contents of that Stream, but ... I don't see you doing that anywhere in the Server-side code you have posted above.

Best Regards,
Danijel Tkalcec
Logged
cdhaene
RTC Expired
*
Posts: 13


« Reply #4 on: September 10, 2017, 11:34:50 PM »

Hi, in fact, the real trouble is that I don't know how to pass a blob to a remote function.

I try to execute an insert query to insert a record in to my remote BD.
this query posts a tiny sqlite-db that was produced by my android-app.
The upload executes a query having as first parameters a blob (that sqlite-db)

On my android device I'm having this now:

Code:
procedure TRTCControl.RTCUpload(aClientDataset : TClientDataSet;
                                aDataModuleName, aQueryName, aSql: string;
                                arParams : array of variant;
                                aStream : TMemoryStream ;
                                aFixedSQL : string='';
                                aOpenDataset : boolean = false;
                                aExecuteDataSet : boolean = False);
var
    i : integer;
    lParams : TrtcArray;
begin
  aClientDataSet.Close;

  aClientDataset.RemoteServer := nil;
  aClientDataset.ProviderName := '';
  with RtcClientModule1 do
  begin
    StartCalls;
    try
      with Data.NewFunction('f_UploadFileToServer') do
      begin
        asLargeInt['clientdataset'] := nativeUint(aClientDataset);
        asString['datamod'] := aDataModuleName;
        asString['query'] := aQueryName;

        if aFixedSQL <> '' then
          asString['fixedsql'] := aFixedSQL
        else
          asString['sql'] := asql;

        lParams := NewArray('Params');

        NewByteStream('stream');
        asByteStream['stream'].CopyFrom(aStream, aStream.Size);
        asByteStream['stream'].Seek(0, soFromBeginning);
        lParams.asValue[0] := asByteStream['stream']; <----------THIS ONE DOESN'T COMPILE

        if length(arParams) > 0 then
        begin
          for i:= low(arParams) to high(arParams) do
          begin
            lParams.asValue[i+1] := arParams[i];
          end;
        end;

        asBoolean['open'] := false;
        asBoolean['execute'] := true;
        astream.SaveToFile(ModeSelectionForm.PathToInternalStorage + 'Database/DATATEST.DB');
      end;
      call(r_initRTCClientDataset);
    finally
      post;

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


« Reply #5 on: September 11, 2017, 07:36:06 AM »

Delphi does NOT automatically convert a TMemoryStream into a Variant, which is why the line of code you've commented "does NOT compile". But ... you are already passing the contents of your "aStream:TStream" variable to your remote function as a "stream" parameter with this code ...

Code:
  with RtcClientModule1 do
    begin
    with Data.NewFunction('f_UploadFileToServer') do
      begin
      NewByteStream('stream'); // create a new Stream object, to be sent as 'stream' parameter of your remote function
      aStream.Seek(0, soFromBeginning); // make sure your source stream (aStream in this case) is at the beginning
      asByteStream['stream'].CopyFrom(aStream, aStream.Size); // copy the contents of "aStream" to your 'stream' parameter
      end;
    Call(r_initRTCClientDataset);
    end;
... which could also be written without using "with", to make it clear what is being called where ...
Code:
  RtcClientModule1.Prepare('f_UploadFileToServer'); // create a new TRtcFunctionInfo object to be used with RtcClientModule1
  aStream.Seek(0, soFromBeginning); // make sure your source stream (aStream in this case) is at the beginning
  RtcClientModule1.Param.NewByteStream('stream'); // create a new Stream object to be sent as 'stream' parameter of the remote function
  RtcClientModule1.Param.asByteStream['stream'].CopyFrom(aStream, aStream.Size); // copy the contents of "aStream" to your 'stream' parameter
  RtcClientModule1.Call(r_initRTCClientDataset);
Anyway ... that's not the problem. What you are missing is the Server-side code to extract that stream. You can access that 'stream' parameter on the Server from the "OnExecute" event of the TRtcFunction object and copy the 'stream' contents into any other "aStream:TStream" variable like this ...
Code:
  if Param.isType['stream']=rtc_ByteStream then // make sure that 'stream' parameter actually has a rtcByteStream 
    begin
    Param.asByteStream['stream'].Seek(0,soFromBeginning); // move source stream to the beginning
    aStream.CopyFrom(Param.asByteStream['stream'],Param.asByteStream['stream'].Size); // copy contents from 'stream' parameter to 'aStream' object
    end;
PS. The name of the variable is 'stream' here, only because you've used it in your example, but it can be anything else. You can also send the contents of Streams inside Arrays, but then you should use the NewByteStream method on the TRtcArray object (or TRtcRecord or TRtcDataSet or any other object that can hold multiple elements) instead of directly creating it on the TRtcFunctionInfo object (which was, in your example above, returned from Data.newFunction('f_UploadFileToServer') and used in the "with" block).

Best Regards,
Danijel Tkalcec
Logged
cdhaene
RTC Expired
*
Posts: 13


« Reply #6 on: September 11, 2017, 07:47:39 AM »

hooo. Difficult to understand...
So, server-side, I'll have this code, is that correct?

Code:
procedure TServerContainer.f_UploadFileToServerExecute(Sender: TRtcConnection;
  Param: TRtcFunctionInfo; Result: TRtcValue);
var
  rtcDataset : TrtcDataset;
  arParams : array of variant;
  i : integer;
  myQuery : TUniQuery;
  aStream : TStream;

  function assignMyQuery(aDatamod, aQuery : string) : TUniQuery;
  var
    i : integer;
    j : integer;
  begin
    Result := nil;
    for i:= 0 to application.ComponentCount-1 do
    begin
      if application.Components[i] is TDataModule then
      begin
        if lowercase(TDataModule(application.Components[i]).Name) = lowercase(aDataMod) then
        begin
          for j:= 0 to application.Components[i].ComponentCount-1 do
            if application.Components[i].Components[j] is TUniQuery then
            begin
              if lowercase(TUniQuery(application.Components[i].Components[j]).Name) = lowercase(aQuery) then
                exit(TUniQuery(Application.Components[i].Components[j]));
            end;
        end;
      end;
    end;
  end;

  function GetQuery(AQuery: String): string;
  begin
    with qGetQuery do
    begin
      Close;
      Params[0].AsString := AQuery;
      Open;
      Result := FieldByName('SQL').AsString;
    end;
  end;

begin
  try
    myQuery := assignmyQuery(Param.asText['datamod'], Param.asText['query']);
    if myQuery = nil then
    begin
      dbLog('ERROR',format('Error ! : The query %s on datamodule %s doesnot exist !',[Param.asText['query'], Param.asText['datamod']]));
      addLineToLog(format('Error ! : The query %s on datamodule %s doesnot exist !',[Param.asText['query'], Param.asText['datamod']]));
      Result.NewException;
    end
    else
    begin
      myQuery.close;
      if Param.asText['fixedsql'] <> '' then
      begin
        myQuery.sql.Text := Param.asText['fixedsql'];
      end
      else if Param.asText['sql'] <> '' then
      begin
        myQuery.sql.Text := getQuery(Param.asText['sql']);
      end;

      if (Param.asArray['params'].Count) > 0 then
      begin
        for i:= 0 to Param.asArray['params'].Count-1 do
        begin
          if Param.isType['stream']=rtc_ByteStream then
          begin
            aStream := TStream.Create;
            Param.asByteStream['stream'].Seek(0,soFromBeginning);
            aStream.CopyFrom(Param.asByteStream['stream'],Param.asByteStream['stream'].Size);
            Param.asByteStream['stream'] := aStream;
            aStream.Free;
          end
          else
            myQuery.params[i].value := Param.asArray['params'][i];
        end;
      end;

      if Param.asBoolean['open'] = True then
      begin
        myQuery.active := True;

        DelphiDataSetToRtc(myQuery, result.NewDataSet);
      end
      else if Param.asBoolean['execute'] = True then
      begin
        myQuery.execute;
        Result.asBoolean := True;
      end;
    end;
  except
    on E: Exception do
    begin
      dbLog('ERROR', format('exception ! %s %s',[E.ClassName, E.Message]));
      AddLineToLog(format('exception ! %s %s',[E.ClassName, E.Message]));
      result.asException := 'exception !';
    end;
  end;
end;
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #7 on: September 11, 2017, 08:12:23 AM »

You are making things more complicated than they are.

On the Server, the "Param" object is basically the same object you have created by calling Data.newFunction() or Prepare() on the Client. I think the main source of misunderstanding here is the use of "with" statements, which make it unclear which methods are being called on which objects. So ... let's start by removing all "with" statements on the Client, to make it clear what you are actually doing.

This code on the Client ...
Code:
  with RtcClientModule1 do
    begin
    with Data.NewFunction('f_UploadFileToServer') do
      begin
      NewByteStream('stream'); // create a new Stream object, to be sent as 'stream' parameter of your remote function
      aStream.Seek(0, soFromBeginning); // make sure your source stream (aStream in this case) is at the beginning
      asByteStream['stream'].CopyFrom(aStream, aStream.Size); // copy the contents of "aStream" to your 'stream' parameter
      end;
    Call(r_initRTCClientDataset);
    end;
... is equivalent to this (if we remote all "with" statements) ...
Code:
  // create a new TRtcFunctionInfo object to be used with RtcClientModule1.
  RtcClientModule1.Data.NewFunction('f_UploadFileToServer');

  { Since the object created above is a Remote Function,
     it is accessible here using the "RtcClientModule1.Param" property ... }

  // Create a new ByteStream object as a 'stream' parameter of the remote function object we have just created above ...
  RtcClientModule1.Param.NewByteStream('stream');

  // Copy the contents of "aStream:TStream" to our 'stream' object ...
  aStream.Seek(0, soFromBeginning); // move source stream to the beginning
  RtcClientModule1.Param.asByteStream['stream'].CopyFrom(aStream, aStream.Size); // copy the "aStream" contents

  // Call the remote function ...
  RtcClientModule1.Call(r_initRTCClientDataset);

Now, all you have to know is that "RtcClientModule1.Param" object on the Client will be sent to the Server with the "Call" method and the same object will then be accessible on the Server using the "Param" object passed to the "OnExecute" event.

So ... if you are using the code above to send a single Stream to the Server, then you should use something like this to access that Stream from the "OnExecute" event on the Server ...
Code:
  // make sure that 'stream' parameter actually has a rtcByteStream 
  if Param.isType['stream']=rtc_ByteStream then
    begin
     // move source stream to the beginning
    Param.asByteStream['stream'].Seek(0,soFromBeginning);
    // copy contents from 'stream' parameter to 'aStream' object
    aStream.CopyFrom(Param.asByteStream['stream'],Param.asByteStream['stream'].Size);
    end;

As you can see, there is no Array here. And why should there be an Array, if you have created your 'stream' as a normal parameter of your remote function? Even though you are sending some of your parameters in the "params" array in your example code above, the "stream" object is NOT part of that array, since you have created it directly on the remote function object and is therefor passed on the same level as the "params" array and all the other parameters you are passing to the remote function (like "sql", "fixedsql" and so on). The fact that it's a Stream does NOT make it any different from any other data type you are passing as parameters.

Best Regards,
Danijel Tkalcec
Logged
cdhaene
RTC Expired
*
Posts: 13


« Reply #8 on: September 11, 2017, 08:36:30 AM »

I understand your array-remark.
But, the query I execute has 7 parameters, so it's an array containing 7 parameters.
The first on (index 0) is the blob
Logged
cdhaene
RTC Expired
*
Posts: 13


« Reply #9 on: September 11, 2017, 10:01:11 AM »

Danijel, don't want to be annoying but I can't make it work... Sorry
Is there smething I don't see? Something I'm missing?
At serverside I got a exception ! EWriteError Stream write error

Client-code:
Code:
procedure TRTCControl.RTCUpload(aClientDataset : TClientDataSet;
                                aDataModuleName, aQueryName, aSql: string;
                                arParams : array of variant;
                                aStream : TMemoryStream ;
                                aFixedSQL : string='';
                                aOpenDataset : boolean = false;
                                aExecuteDataSet : boolean = False);
var
    i : integer;
    lParams : TrtcArray;
begin
  aClientDataSet.Close;

  aClientDataset.RemoteServer := nil;
  aClientDataset.ProviderName := '';
  with RtcClientModule1 do
  begin
    StartCalls;
    try
      with Data.NewFunction('f_UploadFileToServer') do
      begin
        asLargeInt['clientdataset'] := nativeUint(aClientDataset);
        asString['datamod'] := aDataModuleName;
        asString['query'] := aQueryName;

        if aFixedSQL <> '' then
          asString['fixedsql'] := aFixedSQL
        else
          asString['sql'] := asql;

        lParams := NewArray('Params');

        NewByteStream('stream');
        asByteStream['stream'].CopyFrom(aStream, aStream.Size);
        asByteStream['stream'].Seek(0, soFromBeginning);
        //lParams.asValue[low(arParams)] := asByteStream['stream'];

        if length(arParams) > 0 then
        begin
          for i:= low(arParams) to high(arParams) do
          begin
            lParams.asValue[i+1] := arParams[i];
          end;
        end;

        asBoolean['open'] := false;
        asBoolean['execute'] := true;
        astream.SaveToFile(ModeSelectionForm.PathToInternalStorage + 'Database/DATATEST.DB');
      end;
      call(r_initRTCClientDataset);
    finally
      post;

      waitforCompletion;
    end;
  end;
end;

Server-code :
Code:
procedure TServerContainer.f_UploadFileToServerExecute(Sender: TRtcConnection;
  Param: TRtcFunctionInfo; Result: TRtcValue);
var
  rtcDataset : TrtcDataset;
  arParams : array of variant;
  i : integer;
  myQuery : TUniQuery;
  aStream : TStream;

  function assignMyQuery(aDatamod, aQuery : string) : TUniQuery;
  var
    i : integer;
    j : integer;
  begin
    Result := nil;
    for i:= 0 to application.ComponentCount-1 do
    begin
      if application.Components[i] is TDataModule then
      begin
        if lowercase(TDataModule(application.Components[i]).Name) = lowercase(aDataMod) then
        begin
          for j:= 0 to application.Components[i].ComponentCount-1 do
            if application.Components[i].Components[j] is TUniQuery then
            begin
              if lowercase(TUniQuery(application.Components[i].Components[j]).Name) = lowercase(aQuery) then
                exit(TUniQuery(Application.Components[i].Components[j]));
            end;
        end;
      end;
    end;
  end;

  function GetQuery(AQuery: String): string;
  begin
    with qGetQuery do
    begin
      Close;
      Params[0].AsString := AQuery;
      Open;
      Result := FieldByName('SQL').AsString;
    end;
  end;

begin
  try
    myQuery := assignmyQuery(Param.asText['datamod'], Param.asText['query']);
    if myQuery = nil then
    begin
      dbLog('ERROR',format('Error ! : The query %s on datamodule %s doesnot exist !',[Param.asText['query'], Param.asText['datamod']]));
      addLineToLog(format('Error ! : The query %s on datamodule %s doesnot exist !',[Param.asText['query'], Param.asText['datamod']]));
      Result.NewException;
    end
    else
    begin
      myQuery.close;
      if Param.asText['fixedsql'] <> '' then
      begin
        myQuery.sql.Text := Param.asText['fixedsql'];
      end
      else if Param.asText['sql'] <> '' then
      begin
        myQuery.sql.Text := getQuery(Param.asText['sql']);
      end;

      if (Param.asArray['params'].Count) > 0 then
      begin
        for i:= 0 to Param.asArray['params'].Count-1 do
        begin
          if Param.isType['stream']=rtc_ByteStream then
          begin
            aStream := TStream.Create;
            Param.asByteStream['stream'].Seek(0,soFromBeginning);
            aStream.CopyFrom(Param.asByteStream['stream'],Param.asByteStream['stream'].Size);
            aStream.Seek(0, soFromBeginning) ;
            myquery.Params[i].AsStream := aStream;
            aStream.Free;
          end
          else
            myQuery.params[i].value := Param.asArray['params'][i];
        end;
      end;

      if Param.asBoolean['open'] = True then
      begin
        myQuery.active := True;

        DelphiDataSetToRtc(myQuery, result.NewDataSet);
      end
      else if Param.asBoolean['execute'] = True then
      begin
        myQuery.execute;
        Result.asBoolean := True;
      end;
    end;
  except
    on E: Exception do
    begin
      dbLog('ERROR', format('exception ! %s %s',[E.ClassName, E.Message]));
      AddLineToLog(format('exception ! %s %s',[E.ClassName, E.Message]));
      result.asException := 'exception !';
    end;
  end;
end;
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #10 on: September 11, 2017, 10:24:51 AM »

If you want to have the ByteStream inside your 'params' Array at element 0, you should call the NewByteStream(0) method on the Array object (which would be "lParams" in your example above). So, here is that code for the Client (I'm avoiding the use of the "with" statement here for clarity reasons) ...

procedure TRTCControl.RTCUpload(<...snip...>
                                arParams : array of variant;
                                aStream : TMemoryStream;
                                <...snip...>);
  var
    i : integer;
    lParams : TRtcArray;
  begin
  { the next line is similar to calling RtcClientModule1.Data.NewFunction('f_UploadFileToServer'),
     but it also clears the "Data" object, in case they it was left assigned, which could happen
     in case of an exception while preparing any previous remote function call ... }
  RtcClientModule1.Prepare('f_UploadFileToServer');

  { the next line creates an Array as the "Params" parameter for the remote function
    and is basically the exact same thing as what you did in your example code, but
    uses the "Param" object on the RtcClientModule instead of using the "with" statement
    to imply access to the TRtcFunctionInfo object created with a call to Data.NewFunction() ... }
  lParams := RtcClientModule.Param.NewArray('Params');

  { the next line creates a ByteStream in the "Params" Array at index 0,
     which is what you actually whated to do, but have instead created a ByteStream
     as a 'stream' parameter of the remote function object using NewByteStream('stream') ... }
  lParam.NewByteStream(0);

   { the next 2 lines move the source "aStream" to the beginning and
      then copy the contents of the "aStream" into the ByteStream ... }
    aStream.Seek(0,soFromBeginning);
    lParam.asByteStream[0].CopyFrom(aStream, aStream.Size);

   { the rest of your Client-side code was correct from the start ... }
      if length(arParams) > 0 then
          for i:= low(arParams) to high(arParams) do
          begin
            lParams.asValue[i+1] := arParams[ i ];
          end;
<...snip...>

Now, you have the Stream at index 0 of your 'params' array (and your other parameters at indexes 1 and above), so you could do something like this on the Server to read it all ...

  var lArray:TRtcArray; // temp variable for our "params" array
        aStream:TStream; // wherever we want the Stream
  begin
  if Param.isType['params']=rtc_Array then // make sure we have received an Array
    begin
    lArray:=Param.asArray['params'];
    if lArray.Count > 0 then
      for i:= 0 to lArray.Count-1 do
        if lArray.isType[ i ]=rtc_ByteStream then
          begin
          // aStream := HuhHuh <---- YOUR CODE COMES HERE

          { <---- HERE, you need to set "aStream" to your destination Stream!
             Since you have sent some data to the Server, I guess you know what you want to do with it.
             The stream contents is here and below is the code to copy it into any other TStream,
              but copying it into some TStream which you are just going to destroy makes no sense.            
             Anway ... Here is to code which would copy that Stream to "aStream" (which you have to initialize before) ... }

          lArray.asByteStream[ i ].Seek(0,soFromBeginning);
          aStream.CopyFrom(lArray.asByteStream[ i ],lArray.asByteStream[ i ].Size);
          end
       else
          <SomethingThatExpectsYourVariantValue> := lArray.asValue[ i ];
    end;
  end;

Best Regards,
Danijel Tkalcec
Logged
cdhaene
RTC Expired
*
Posts: 13


« Reply #11 on: September 11, 2017, 12:50:50 PM »

Danijel,

the purpose of my call is the execution of a query.
This query inserts a record in a table on the database (MySQL).
the first field is a blob (the stream) containing a small sqlite-db.

the query that will be executed is

INSERT INTO MyTable (bbfw, status, pandaid, sequentienr, naam, resource, versie, bbfw_id) VALUES
(:P0, :P1, :P2, :P3, :P4, :P5, :P6, :P7)

:P0 is the stream

The server-side has to execute this query, inserting a record in a db.
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1881


« Reply #12 on: September 11, 2017, 12:56:27 PM »

In that case, you should definitely take a closer look at Database Access Demos included in the "Demos/DB_Access" folder and check the "rtcDB.pas" unit, which includes functions for copying data to-and-from a TDataSet, including Blob and Graphical fields. There are also example functions for the Server to prepare and execute a Query using parameters received from the Client (also including BLOB fields).

For example, the "BDEDemoSrv.pas" unit is using "rtcPrepareSqlWhere" and "rtcSetSqlWhereParams" functions from the "rtcDB.pas" unit to execute SELECT statements based on the SQL text and parameters received from the Client, and  ... using "rtcPrepareSqlAction" and "rtcSetSqlActionParams" functions to prepare and execute INSERT, DELETE and UPDATE statements on the Server, also using SQL with parameters.

I'm not saying that you should copy/paste that code or even use these functions, but I think you should analyze these functions to see how they work. Then, you can write your own to do exactly what you want.

Best Regards,
Danijel Tkalcec
Logged
cdhaene
RTC Expired
*
Posts: 13


« Reply #13 on: September 11, 2017, 01:23:56 PM »

I'll take a closer look at the DB-Demos.
As far as I understand, this should do the trick at serverside:

      
Code:
        lArray:=Param.asArray['params'];

        if (lArray.Count > 0) then
        begin
          for i:= 0 to lArray.Count-1 do
          begin
            if (Param.isType['stream']=rtc_ByteStream) then
            begin
              lArray.asByteStream[i].Seek(0,soFromBeginning);
              MyQuery.Params[i].LoadFromStream(lArray.asByteStream[i], ftBlob);
            end
            else
              MyQuery.Params[i+1].Value := lArray[i];
          end;
        end;
      


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


« Reply #14 on: September 11, 2017, 02:11:48 PM »

You probably mean like this (see my comments) ...
Code:
       lArray:=Param.asArray['params'];

        if (lArray.Count > 0) then
        begin
          for i:= 0 to lArray.Count-1 do
          begin
            if (lArray.isType[i]=rtc_ByteStream) then // <--- I've changed this line!
            begin
              lArray.asByteStream[i].Seek(0,soFromBeginning);
              MyQuery.Params[i].LoadFromStream(lArray.asByteStream[i], ftBlob);
            end
            else
              MyQuery.Params[i].Value := lArray[i]; // <--- and changed this line!
          end;
        end;
      

Best Regards,
Danijel Tkalcec
Logged
Pages: [1] 2
  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.034 seconds with 16 queries.