2
votes

I have searched high and low for a working solution for sending an image (e.g. tpngimage) to the server using a datasnap method - but I cannot make it to work.

When I load the image and save it to a memorystream, I'm able to read the image back from the stream - locally in the client, which is not really a surprise. But when the server method gets called, the stream is nil and contains nothing, the other parameters are fine (simple datatype and an object).

Did I miss something obvious here? I was under the impression that TStream is a valid datatype for datasnap methods, but maybe I'm wrong?

The from client side it looks like this.

function TPerson.Update: Boolean;
var
  AStream : TMemoryStream;
  APicture : TPngImage;
  ASize : Integer;
begin
  if (FId > 0) then // if Id below zero we have a problem
  begin

    ClientModule1.ServerMethods1Client.UpdatePerson(Self);
    APicture := TPngImage.Create;
    AStream := TMemoryStream.Create;
    try
      // Temp just use a file
      AStream.LoadFromFile('.\images\075.png');
      ASize := AStream.Size;
      AStream.Position := 0; // wind back if needed

      // just for testing, we can read back the image from the stream
      APicture.LoadFromStream(AStream);

      ClientModule1.ServerMethods1Client.UpdatePersonPicture(self, ASize, AStream);

    finally
      FreeAndNil(AStream);
      FreeAndNil(APicture);
    end;

  end;
  FModified := False;
end;

And the proxy method looks like this

procedure TServerMethods1Client.UpdatePersonPicture(APerson: TPerson; ASize: Integer; APictureStream: TMemoryStream);
begin
  if FUpdatePersonPictureCommand = nil then
  begin
    FUpdatePersonPictureCommand := FDBXConnection.CreateCommand;
    FUpdatePersonPictureCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    FUpdatePersonPictureCommand.Text := 'TServerMethods1.UpdatePersonPicture';
    FUpdatePersonPictureCommand.Prepare;
  end;
  if not Assigned(APerson) then
    FUpdatePersonPictureCommand.Parameters[0].Value.SetNull
  else
  begin
    FMarshal := TDBXClientCommand(FUpdatePersonPictureCommand.Parameters[0].ConnectionHandler).GetJSONMarshaler;
    try
      FUpdatePersonPictureCommand.Parameters[0].Value.SetJSONValue(FMarshal.Marshal(APerson), True);
      if FInstanceOwner then
        APerson.Free
    finally
      FreeAndNil(FMarshal)
    end
    end;
  FUpdatePersonPictureCommand.Parameters[1].Value.SetInt32(ASize);
  FUpdatePersonPictureCommand.Parameters[2].Value.SetStream(APictureStream, FInstanceOwner);
  FUpdatePersonPictureCommand.ExecuteUpdate;
end;

The Server method looks like this - it fails due to the APictureStream is nil.

procedure TServerMethods1.UpdatePersonPicture(APerson: TPerson; ASize: integer;
  APictureStream: TMemoryStream);
var
  APicture : TPngImage;
begin
  fdqPersons.Close;
  fdqPersons.SQL.Clear;
  fdqPersons.Connection.StartTransaction;
  try
    fdqPersons.SQL.Add('update Persons set Picture=:Picture ');
    fdqPersons.SQL.Add('where Id=:Id');
    fdqPersons.ParamByName('Id').Value := APerson.Id;

    APicture := TPngImage.Create;
    try
       // APicture for testing - but APictureStream is nil!
       APicture.LoadFromStream(APictureStream);
       fdqPersons.ParamByName('Picture').Assign(APicture);
       fdqPersons.ExecSQL;
    finally
      FreeAndNil(APicture);
    end;
    fdqPersons.Close;
    fdqPersons.Connection.Commit;
    LogEvent(format('Person picture updated ID: %d',[APerson.id]));
  except
    on e:exception do
    begin
      fdqPersons.Connection.Rollback;
      LogEvent(format('Error updating person picture %s',[e.Message]));
      raise;
    end;
  end;
end;
1
On the client side, nothing is being done with APicture, although that's not your issue. In reality, you're not "sending a TPngImage " as your question describes. You're simply sending a file.Jerry Dodge
Yes, I have tried different solutions, it doesn't matter for now if it's a file or an image. I would just like to get some stream data. Now, interesting enough, I just have removed this line client side... APicture.LoadFromStream(AStream); And vupti - now there are available data on the server side....hhaumann

1 Answers

3
votes

When you call APicture.LoadFromStream(AStream); the stream's position goes to the end, and therefore when passing it into ClientModule1 it does not have any data left to be read. Either get rid of the unnecessary portion where you write the stream to a TPngImage or reset the stream's position back to 0 just after that part.