TXDataWebDataSet and picture

Hi!


Is there any demo about TWebDatasSet and pictures? I'm looking on how to upload a picture to a dataset, save it and later display it.

Also, what should be the type of the field in the dataset? I'm trying to create an web app where users can upload pictures.

I checked the demos but didnt' find any info about uploading pictures to dataset.

Kind regards,
Dino

Check the following example to learn how to handle blobs with XData and TMS Web Core: 

http://download.tmssoftware.com/business/resources/Example_WebCore_Blob.zip

I will, thank you!

Hmm.. I'm stuck :(

Here's what I did so far:



procedure TfrmNew.UpdatePicture;
var
  xhr: TJSXmlHttpRequest;
begin
  xhr := TJSXMLHttpRequest.new;
//  xhr.open('PUT', connServer.URL+'/'+string(TJSObject(wdsPobuda.CurrentData)['Slika@xdata.proxy']));
  xhr.open('PUT', connServer.URL+'/'+string('pobuda(1)/Slika'));  //<-- like this because TJSObject(wdsPobuda.CurrentData) is always empty!
  xhr.send(imgSlika.Base64Image);
end;



procedure TfrmDetails.ShowPicture;
var
  xhr: TJSXmlHttpRequest;


  procedure _Load;
  var stream: TBytesStream;
  begin
    stream := TBytesStream.Create(DecodeBase64(xhr.responseText));
    imgSlika.Picture.LoadFromStream(stream);  
  end;


begin
  xhr := TJSXMLHttpRequest.new;
//  xhr.open('GET', connServer.URL+'/'+string(TJSObject(wdsPobuda.CurrentData)['Slika@xdata.proxy']));
  xhr.open('GET', connServer.URL+'/pobuda(1)/Slika'); //<-- like this because TJSObject(wdsPobuda.CurrentData) is always empty!
  xhr.addEventListener('load', @_Load);
  xhr.send;
end;


The problems are:

1. I don't know how to encode the picture as Base64. I need to decode it because I wnt to assign the 
2. Unkown 'TBytesStream'

How to get saved data after ApplyUpdate (please note, than I'm adding a new record before calling ApplyUpdate).

I tought I could do this (simplified version):



  wdsPobuda.QueryString := '$filter=Id lt ''1''';
  wdsPobuda.Load;

  wdsPobuda.Append;
//  wdsPobuda.FieldByName('Id').AsInteger := autogenerated on insert
  wdsPobuda.FieldByName('Description').AsInteger := 'test';
  wdsPobuda.Post;
  wdsPobuda.ApplyUpdates;
  
  ShowMEssage(wdsPobuda.FieldByName('Id').AsString); <-- this shows the latest Id after the insert.



I don't know how to get the Id of the just inserted record. I need it because I want to execute the procedure UpdatePicture.



What type is imgSlika?

What XData version are you using?

XData: latest version (updated few days ago).


imgSlika: tried 2 ways

First try: imgSlika: TWebImageControl,
now using imgSlika: TWebDBImageControl;

To display the picture I use the TWebDBImageControl linked to a field "Slika" of the TXDataWebDataset


I execute this code:
  wdsPobuda.QueryString := '$filter=Id eq '''+IntToStr(PobudaId)+'''&$expand=Slika';
  wdsPobuda.Load;

and get this error onthe bottom of the page:

ERROR
Invalid integer value: "$ZC" | fMessage::Invalid integer value: "$ZC" fHelpContext::0
at http://localhost:8000/Okolje24/Okolje24.js [3138:12]

Please note that the query (http://localhost:3000/xdata/Pobuda?$filter=Id%20eq%20'28'&$expand=Slika)  executes correctly, the problem is in the displaying of the data.

I previously saved the data using this code

procedure TfrmNew.UpdatePicture;
var
  xhr: TJSXmlHttpRequest;
begin
  xhr := TJSXMLHttpRequest.new;
  xhr.open('PUT', connServer.URL+'/'+string('pobuda('+wdsPobuda.FieldByName('Id').AsString+')/Slika'));
  xhr.send(imgSlika.Base64Image);
end;


I have checked the documentation for TWebDBImageControl,but there is only a minimal description and I haven't fount an example in the XData subfolder.

What is the correct method for saving and displaying images via XDataWebDataSet?


You can load the image directly into the TWebImageControl using this code:


  WebImageControl1.Url := XDataWebConnection1.URL + '/' +
    string(TJSObject(XDataWebDataset1.CurrentData)['Photo@xdata.proxy']);

Done, but the picture is still missing (but no errors in browser console). For double check - here's how I save the picture:



procedure TfrmNew.UpdatePicture;
var
  xhr: TJSXmlHttpRequest;
begin
  xhr := TJSXMLHttpRequest.new;
  xhr.open('PUT', connServer.URL+'/'+string('pobuda('+wdsPobuda.FieldByName('Id').AsString+')/Slika'));
  xhr.send(imgSlika.Base64Image);
end;


This is called on the AplyUpdates event:


procedure TfrmNew.wdsPobudaAfterApplyUpdates(Sender: TDataSet; Info: TResolveResults);
begin
  UpdatePicture;
  ModalResult := mrOk;
end;


After execution the DB record is filled with text (Base64 image data I presume :) )

I load it later (in another form) with this code:


procedure TfrmDetails.wdsPobudaAfterOpen(DataSet: TDataSet);
begin
  imgSlika2.Url := connServer.URL + '/' +string(TJSObject(wdsPobuda.CurrentData)['Slika@xdata.proxy']);
end;


I checked the browser console for any errors, but ther seems everything ok - just the picture is missing.

You should send the raw binary image to the server, not the base64 value. This is what you should use:




  function Base64ToArrayBuffer(str: string): TJSArrayBuffer;
  var
    BufView: TJSUInt8Array;
    BinaryString: string;
    I: Integer;
  begin
    BinaryString := window.atob(str);
    Result := TJSArrayBuffer.new(Length(BinaryString));
    BufView := TJSUInt8Array.new(Result);
    for I := 0 to Length(BinaryString) - 1 do
      BufView := TJSString(BinaryString).charCodeAt(I);
  end;



  xhr.send(Base64ToArrayBuffer(WebImageControl1.Base64Image));

It's working!


I just needed to fix a little bit the code:

Wrong:
BufView := TJSString(BinaryString).charCodeAt(I);

correct:
BufView := TJSString(BinaryString).charCodeAt(I);

Mistake - correct is:


BufView := TJSString(BinaryString).charCodeAt(I);

I found a bug in the forum - you cannot add text with square brackets :)


Replace the curly brackets witj the square brackets: BufView{I}

Note that you can uncheck the BBCodes checkbox below the message box to enable the use of square brackets.

Example:
BufView[I] := TJSString(BinaryString).charCodeAt(I);

ops.. :)

I think the code has a strange thing



  function Base64ToArrayBuffer(str: string): TJSArrayBuffer;
  var
    BufView: TJSUInt8Array;
    BinaryString: string;
    I: Integer;
  begin
    BinaryString := window.atob(str);
    Result := TJSArrayBuffer.new(Length(BinaryString));
    BufView := TJSUInt8Array.new(Result);
    for I := 0 to Length(BinaryString) - 1 do
      BufView := TJSString(BinaryString).charCodeAt(I);
  end;


Why the part in red? And also has an error in line BufView := TJSString(. 
It does nothing to the result? 

Can you rewrite the procedure, because I don't know what to do with the red part - should I just remove it?

No, it's needed to convert the characters to actual byte value (a string to an array of bytes).

Hi
If you are using sqlserver database in your application
you can use sqlserver filetable and then share vitural directory of sqlserver filetable on IIS
then load and save picture easily on it.

1 Like

An old topic, a supplementary question.

I'm working on the "Xdata-blob-webcore" example.

My XData Server needs Authorization. (Bearer token).

When I load the image, the central "OnConnectionRequest" probably doesn't go through because I get an "Unauthorized" error message.
In the debugger, I see the right URL.
A test in "Postman" with token works perfectly.

How can I send the token? Or what do I have to change in the example? Is there another "connection" here?

I believe you are referring to this code which sets an image:

  WebImageControl1.Url := XDataWebConnection1.URL + '/' +
    string(TJSObject(XDataWebDataset1.CurrentData)['Photo@xdata.proxy']);

In this case, the event will not be fired indeed because you are just setting an URL for the browser to load the image. You will have to load the image bytes directly from JavaScript and then set the image data in the URL:

procedure TForm1.WebButton6Click(Sender: TObject);
var
  xhr: TJSXmlHttpRequest;

  procedure _Load;
  begin
    WebImageControl1.Url := TJSURL.createObjectURL(xhr.response);
  end;

begin
  xhr := TJSXMLHttpRequest.new;
  xhr.open('GET',
    XDataWebConnection1.URL + '/' +
    string(TJSObject(XDataWebDataset1.CurrentData)['Photo@xdata.proxy'])
  );
  xhr.responseType := 'blob';
  xhr.addEventListener('load', @_Load);
  xhr.send;
end;

With the code above, you can set any headers you need, including the authorization one.