1

I have a mobile delivery application using Rad Server that allows the user to scan a barcode and take pictures of the delivery from their phone and then send them to the server which stores the picture in a specific directory. It then stamps the file name to the order for future reference.

Now I would like the user to access the picture using a pathname parameter and display it on the mobile app when required. I have tried to google an acceptable solution but have not found anything that works.

I did find this post which seems to be what I want but I can't get it to work:

RAD Server 10.4.2 Duplicate Endpoints

and I checked out David Intersimone's youtube video. Using his example on the server side gives me the following error when I include the code:

[ResourceSuffix('*')]
[EndpointProduce ('image/jpeg')]
procedure GetImage(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);

enter image description here

If I remove the 'EndpointProduce' line I get the following error message:

enter image description here

The code I am executing is:

procedure TdmCapture.GetImage(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var fs: TFileStream;
begin
//  FileName := ARequest.Params.Values['FileName'];
//  fs := TFileStream.Create(FileName, fmOpenRead);
  fs := TFileStream.Create('c:\temp\Montreal.jpg', fmOpenRead);
  AResponse.Body.SetStream(fs, 'image/jpeg', True);
end;

The objective is to send a request to the server from the mobile device with the full pathname of the image. The server then accesses the image and sends it back to the mobile app to be displayed.

I am running Delphi 11 on Windows 10 Pro.

I am testing using jpg files but but what I really want to do is implement the solution for bmp files.

I have also tried to remove the 'EndpointProduce' Line and go with the following code:

  FileName := ARequest.Params.Values['FileName'];
  FileName := 'c:\temp\Montreal.jpg';

  fs := TFileStream.Create(FileName, fmOpenRead);
  AResponse.Headers.SetValue('Content-Type', 'application/json; charset=utf-8');
  AResponse.Body.SetStream(fs, 'image/jpeg', True);

This produces the following error message which I have also not found a solution for:

enter image description here

I modified my Server side code to use a JSON Array as follows:

procedure TdmCapture.GetImage(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var ms: TMemoryStream;
    FileName: string;
    jsonArray: TJSONArray;
begin
  FileName := ARequest.Params.Values['FileName'];
  ms := TMemoryStream.Create;

  try
    ms.LoadFromFile(FileName);

    jsonArray := TJSONArray.Create;
    jsonArray := TDBXJSONTools.StreamToJSON(ms, 0, ms.Size);
    AResponse.Body.SetValue(jsonArray, true);
  finally
    ms.Free;
  end;
end;

This appears to work as when I run it from a browser I get a large array of values.

Obviously, I have to modify my Client side call to process the JSON Array. However, I am unsure how to do that. When I execute the 'GetData' call I receive the error:

enter image description here

My client code is as follows:

  dmCaptureClient.EMSFDClientImages.Resource := 'Capture/GetImage';
  dmCaptureClient.EMSFDClientImages.GetEndpoint.AddParameter('FileName', dmCaptureClient.FDMTFAAttach.FindField('COMMAND').AsString);
  dmCaptureClient.EMSFDClientImages.GetData;


  if not dmCaptureClient.FDMTImages.EOF then
  begin
// Load the image into a TImage component
  end

The components I use are TFDMemTable, TEMSFireDACClient, TFDSchemaAdapter and TFSTableAdpater.

enter image description here

I tried a different approach of using a TBackendEndPoint Component to convert the JSON passed from the server to an image using the following code:

procedure TdmCaptureClient.BackendEndpointGetImageAfterExecute(Sender: TCustomRESTRequest);
var s: TStream;
    JSONArray: TJSONArray;
    myImage: TImage;
begin
  JSONArray := TJSONObject.ParseJSONValue(BackendEndpointGetImage.Response.JSONText) as TJSONArray;

   try
     s := TDBXJSONTools.JSONToStream(jsonArray);
     try
       s.Position := 0;
       myImage.Bitmap.LoadFromStream(s);
     finally
       s.Free;
     end;
   finally
     jsonArray.Free;
   end;
end;

The JSONArray is populated with data but it appears that the TDBXJSONTools.JSONToStream function returns an empty stream and then I get an AV when I try to load the stream into my image.

I seem to recall another post that said there was a problem with TDBXJSONTools.JSONToStream.

Leonard M.
  • 179
  • 3
  • 17
  • It looks like you're trying to stream a .bmp file with a jpeg MIME type. Have you tried 'image/bmp' (not sure Rad Server supports it, but it's worth a try. Also, if you try streaming a .jpg file with image/jpeg, does it work? – John Easley Jul 13 '22 at 14:58
  • @JohnEasley. That was my mistake on the posting. I have edited the posting to show I was using a jpg with image/jpeg. I have been trying a number of different options. I also tried using a bmp file using image/bmp and get the first error about 'cannot produce requested MIME type'. – Leonard M. Jul 14 '22 at 15:44
  • I have edited the post to include another option I found in a post where I removed the EndpointProduce line and set the content type in the header. But that produced a different error. – Leonard M. Jul 14 '22 at 17:21
  • Seems like David's doing some Rad Server trickery for an html page hosted within Rad Server. I think what you need to do is convert your image stream to JSON, then in your mobile app convert the JSON back to an image stream. If you want me to post an example as an answer I will. But there's a question similar already here on SO with a good example https://stackoverflow.com/questions/16550915/how-to-convert-a-json-string-to-an-image – John Easley Jul 15 '22 at 13:55
  • @JohnEasly. I did come across this post but I still had a problem determining how to access the data from the client side. I modified my server side code to use a JSON Array. I edited my question to show the code and error message. I guess I need to change my client side call but I am not sure how to do that. Also, is converting to JSON and back again the most efficient way to accomplish this? – Leonard M. Jul 18 '22 at 17:14
  • I doubt it's the most efficient way, and I don't know what components you're using in your mobile app. To convert the JSON array back to an image, you just reverse the process pretty much. `jsonArray := TJSONObject.ParseJSONValue(jsonString) as TJSONArray; try Stream := TDBXJSONTools.JSONToStream(jsonArray); try Stream.Position := 0; myImage.Picture.Graphic.LoadFromStream(Stream); finally Stream.Free; end; finally jsonArray.Free; end;` – John Easley Jul 18 '22 at 22:15
  • @JohnEasley. I guess my issue stems from a lack of some basic processing knowledge as to how to retrieve and manipulate the data being returned to the client. I found examples of how the code should look as you described above. However, until now I would load the returned data into a TFDMemTable utilizing a TEMSFireDACClient,, TFDSchemaAdapter and TFSTableAdpater and calling the TEMSFireDACClient.GetData method. Perhaps these aren't the right components to use in this case. I thought I would be able to find examples in the samples provided or online. Maybe I'm barking up the wrong tree. – Leonard M. Jul 19 '22 at 15:02
  • I didn't realize I had not posted my client code. I will edit my question to include the client side code and components. – Leonard M. Jul 19 '22 at 15:03
  • I edited my post to include a different approach using a TBackendEndpoint but ran into a problem the TDBXJSONTools.JSONToStream function where by the stream that was created was nil. – Leonard M. Jul 20 '22 at 13:09
  • @JohnEasley The code that you posted was actually correct. I was just having a problem with how/where to process the returned JSON. I have posted an answer that worked for me., However, if you would like post an answer with your code, I would be happy to accept it. Thank you for your help. – Leonard M. Jul 20 '22 at 14:07
  • Glad you got it working! I didn't have time yesterday to work on your first method, sorry about that. Most of my projects are working with pure JSON APIs and avoiding dependencies on Firedac. I'm sure your original method can work, just finding documentation is difficult.. No worries about accepting an answer for me, I'm not here for the points, just glad to help. – John Easley Jul 20 '22 at 14:39
  • Please don't post pictures of text - quote the actual text instead, because such text can be found (unlike to pictures displaying text). With 9 years being a member you should have learnt that. – AmigoJack May 29 '23 at 21:54
  • @AmigoJack - I did post bitmaps of the error messages and the datamodule showing the components I was using. As far as I can tell all my code snippets were properly delimited so they showed correctly in the forum. If you can identify the code that was delimited properly, I would be happy to fix it and make sure it does not happen again in the future. – Leonard M. Jun 29 '23 at 13:07
  • That does not refer to why you post pictures of error message texts instead of quoting the error message texts directly. Furthermore you didn't embed one single BMP, but instead PNGs. I'm talking about the question you posted, not any HTTP `POST` you potentially made. – AmigoJack Jun 29 '23 at 15:54

1 Answers1

1

Well, it appears that I was on the right track with using the BackendEndPoint component to process the JSON that was sent from the server.

With the help of Embarcadero support I was able to accomplish the task with the following code on the Client side:

procedure TLumberNowFrame.wwDataGrid1DblClick(Sender: TObject);
var
  ImageValue : TJSONValue;
  ImageStream : TStream;
  Image : TBitmap;
begin
  dmCaptureClient.BackendEndpointGetImage.Params.Items[0].Value := dmCaptureClient.FDMTFAAttach.FindField('COMMAND').AsString;
  dmCaptureClient.BackendEndpointGetImage.Execute;

  ImageValue := dmCaptureClient.BackendEndpointGetImage.Response.JSONValue;
  ImageStream := TDBXJSONTools.JSONToStream(ImageValue as TJSONArray);
  ImageStream.Position := 0;
  try
    Image := TBitmap.Create;
    Image.LoadFromStream(ImageStream);
    Image1.Bitmap := Image;
  finally
    Image.Free;
  end;
end;

The server side code was correct

Leonard M.
  • 179
  • 3
  • 17