Data Gets Converted when passed to CREATE_STREAM - odata

I have created an UI5 Application to read a file and send it to a custom OData Service in the Backend.
onUploadFile: function() {
var oFileUpload =
this.getView().byId("fileUploaderFS");
var domRef = oFileUpload.getFocusDomRef();
var file = domRef.files[0];
var that = this;
var reader = new FileReader();
var ftype = file.type;
reader.readAsArrayBuffer(file);
reader.onload = function(evt) {
var vContent = evt.currentTarget.result
console.log(vContent);
var hex = that.buf2hex(vContent);
that.updateFile(hex, ftype);
}
},
buf2hex: function(buffer) {
return [...new Uint8Array(buffer)]
.map(x => x.toString(16).padStart(2, '0'))
.join('');
}
When I print the content of hex on the console before sending it to the backend, the data starts with 89504e470d0a1a0a0000000d49484 ....
Even before sending the data in the payload to Odata Service it shows the correct data
Here is the Odata Service
Inside the Create Stream the data when received, is getting converted into something else. As a result the image that has been saved is not opening.
I tried to change the Data Type of Content in SEGW to Binary and it did not work. I also tried to convert the data in the create_stream but in vain. At last I tried reading the data in UI5 in different formats but of no use.
This whole Odata service works perfectly fine when I load the data through Postman Application.
Please help me resolve this Issue. Thanks In Advance.

The sap.ui.unified.FileUploader has everything built in. No need for conversions from Buffer to hex.
Make sure that your FileUploader knows where to upload the file
<unified:FileUploader xmlns:unified="sap.ui.unified"
id="fileUploaderFS"
uploadUrl="/sap/opu/odata/sap/Z_TEST_SRV/FileSet"
/>
The attribute uploadUrl points to the media entity for which you implemented the create_stream method.
Then when the upload is triggered via button press, simply get the FileUploader, set the token (for security reasons when doing a POST request), and fire the upload method.
onUploadFile: function () {
const oFileUpload = this.getView().byId("fileUploaderFS");
const sToken = this.getModel("nameOfTheModel").getSecurityToken();
const oTokenParam = new FileUploaderParameter({
name: "x-csrf-token",
value: sToken
});
oFileUpload.removeAllHeaderParameters()
oFileUpload.addHeaderParameter(oTokenParam);
oFileUpload.upload();
}
To use FileUploaderParameter, make sure to import it at the beginning:
sap.ui.define([
// ...,
"sap/ui/unified/FileUploaderParameter"
], function (/*..., */FileUploaderParameter) {
// ...
Now about your File entity. When working with it via create_stream or read_stream, you don't use the entity structure but is_media_resource. This means your entity doesn't need a property content. Or most of the other properties (except a unique id and the mime type). All other properties would only be used if you want to do one of the CRUD methods (which happens almost never when dealing with streams).

Related

Upload Files in UI5 without using Create Stream in Gateway

I'm building an UI5 application where I need to send several entities to backend at the same time (I'm using create_deep_entity method for that).
I'm using the concept of master-detail (father and children). One of these children is a set of files which need to be sent to backend at the same request as other entities (I want to send them together on create deep).
Is there a way I can use an UI5 component (file upload maybe?) to read the files and send to backend without using create stream method (it must be sent along with other entities on create deep)?
Apreciate your input on this
There is no standard UI5 component that can be used to upload files and send them to the backend without using the create stream method. However, you can create a custom component that will allow you to do this.
You will need to create a custom component that will read the files and send them to the backend. This will require some custom coding.
coding example:
var oFileUploader = new sap.ui.unified.FileUploader({
uploadUrl: "your_backend_url",
uploadOnChange: false,
multiple: true
});
oFileUploader.attachChange(function(oEvent){
var aFiles = oEvent.getParameter("files");
for(var i=0; i<aFiles.length; i++){
var oFile = aFiles[i];
var oRequest = new XMLHttpRequest();
oRequest.open("POST", "your_backend_url");
var oFormData = new FormData();
oFormData.append("file", oFile);
oRequest.send(oFormData);
}
});

Get Response from CREATE_STREAM

We upload a document from SAPUI5 to our SAP System using the CREATE_STREAM Method of the oData Service in ABAP. The creation of the document works fine.
What we would like to achieve is to get the response back to SAPUI5. Especially when there is an error during the creation of the document in the backend.
In Frontend we use the uploadSet Control.
...oUploadSet.uploadItem(oItem);
In the Backend we create a message with
...lo_message_container->add_message( iv_msg_type = /iwbep/cl_cos_logger=>error
iv_msg_number = '018'
iv_msg_id = lv_msg_id
iv_add_to_response_header = abap_true
)....
We can find the created message in the error protocol of our gateway server (/IWFND/ERROR_LOG). But how can this message be retrieved in SAPUI5 and used in the MessageManger Control?
We tried the onUploadCompleted Control but we can't find any response data there.
Can somebody explain how the response or a message header from the CREAT_STREAM method can be used in SAPUI5?
The "new" UploadSet control is kinda half-baked imo. The response will get lost in some internal method. This internal method will then trigger onUploadCompleted and you get nothing but useless information.
Lucky for us we can easily overwrite this internal stuff. UploadSet has an aggregation Uploader. We have to provide our own Uploader. Problem solved. Here is the line that needs to be modified.
sap.ui.define([
"sap/m/upload/Uploader",
...
], function (Uploader, ...) {
return Uploader.extend("my.custom.control.Uploader", {
uploadItem: function (oItem, aHeaders) {
// beginning of the method. take it from the official sources
oXhr.onreadystatechange = function () {
const oHandler = that._mRequestHandlers[oItem.getId()];
if (this.readyState === window.XMLHttpRequest.DONE && !oHandler.aborted) {
// we need to return the xhr object. it contains the response!
that.fireUploadCompleted({ item: oItem, xhr: oXhr });
}
};
// .. rest of the method
}
});
});
Use it like this
<mvc:View xmlns:custom="my.custom.control" ....>
<UploadSet items="....">
.....
<uploader>
<custom:Uploader uploadUrl="......"
uploadCompleted=".onUploadCompleted"
uploadStarted=".onUploadStarted" />
</uploader>
</UploadSet>
Edit: Your own uploader also means implementing your own event handlers (uploadAborted, uploadCompleted, uploadProgressed, uploadStarted). See the official documentation for more information about the events.

SAPUI5 oModel.create() - how to post data to the SAP backend?

I got a button where I want to post data to my SAP backend on press-method:
oCellBtnOtherchart.addContent(new sap.ui.commons.Button({
text : "Save",
press : function() {
var sServiceUrl = "/MyEntitSet('0001')";
var oModel = sap.ui.getCore().getModel();
console.log(oModel);
var oParameters = {
"email" : "a",
"lastname" : "b",
"firstname" : "c",
};
oModel.create(sServiceUrl, oParameters);
}
}));
My questions are:
In which method would this request end in backend? I expect MyEntitySet_CREATE_ENTITY()
Why doesnt it work, the error message is: HTTP request failed 405, Method Not Allowed
But why is it 405, is my Service URL Wrong? How do I Post data correctly to the SAP Backend?
SAP Troubleshooting Guide says: 405 Method Not Allowed
o The method specified in the Request-Line is not allowed for the resource
identified by the Request-URI. The response must include an Allow header
containing a list of valid methods for the requested resource. --> This does not help me right now, anybody knows how to include an allow header?
Because there are only few threads on this topic at SO, which in my opinion do not answer the questions I had, I'll share my findings how to pass data to the backend via oModels create method:
First Define a type of your result entity (check your oData-Model to know the attributes, e.g. Name and YourID):
var oEntry = {};
oEntry.YourID = "0001";
oEntry.Name = "Peter";
Then fetch your model:
var oModel = sap.ui.getCore().getModel();
Then execute the create operation thanks to: https://sapui5.netweaver.ondemand.com/docs/api/symbols/sap.ui.model.odata.ODataModel.html
jQuery.sap.require("sap.ui.commons.MessageBox");
oModel.create('/EntitySet', oEntry, null, function(){
sap.ui.commons.MessageBox.show(
sap.ui.commons.MessageBox.alert("Success!");
);
},function(){
sap.ui.commons.MessageBox.alert("Error!");
});
Results in Backend in Method "ENTITYSET_CREATE_ENTITY"-Method, where you can retrieve YourID and Name:
DATA: ls_data TYPE ycl_class_mpc=>ts_entity.
CALL METHOD io_data_provider->read_entry_data
IMPORTING
es_data = ls_data.
WRITE ls_data-name.
WRITE ls_data-yourid.
This example applies to single calls, you can see the result in ABAP is a structure. If you need to pass multiple datasets to the backend you should search for batch processing at https://openui5.hana.ondemand.com/docs/api/symbols/sap.ui.model.odata.ODataModel.html
If you are still looking for a good blog on how to make a batch post then have a look at this post http://scn.sap.com/community/developer-center/front-end/blog/2012/11/18/gateway-batch-calls-from-sapui5

WebAPI: Upload picture & get byte array

I want the user to be able to upload a file via my application. I don't have DB access, all my data calls get completed via a web-service that another person is writing. I needed to secure the web service, so I've consumed it & exposed it via WebAPI, & added OAuth security.
Now to my problem.
I've written the following.
public Task<FileResult> Post()
{
if (Request.Content.IsMimeMultipartContent())
{
var task = Request.Content.ReadAsByteArrayAsync().ContinueWith(
o =>
{
var result = this.Client.UploadPicture(this.UserId, o.Result);
if (result.ResultCode == 0)
{
return new FileResult()
{
Message = "Success",
FileId = result.ServerId
};
}
throw new HttpResponseException(...);
});
return task;
}
...
}
I'm pretty much a noob when it comes to WebAPI & multithreading (I'm not sure why this needs to be handled async? I'm sure there is a reason, but for now I'd just like a working example and get to the why later..).
My code is loosely based on some R&D & samples I've found on the net, but i haven't come across a scenario like I'm needing to complete... Yet it doesn't seem like I'm doing something out of the ordinary...
Upload a file to the server, and pass the image byte[] object to either sql or another service?
In this line
var result = this.Client.UploadPicture(this.UserId, o.Result);
I'm uploading a byte[] array of something....
Then later (the retrieval method works, I've managed to retrieve & view a test image)
When retrieving the byte array of the "image" i uploaded i get an array of idk what.. EG, i get a valid result of something, but it ain't no picture. Which leads me to believe that the uploaded data is bogus :|
O_o
How to get the image byte[]?
Mime Multipart is more than just your array of bytes. It also has metadata and boundary stuff. You need to treat it as MultiPartContent and then extract the image byte array out of that.
Filip has a blog post on the subject here.

What object (key-value) can I use for actionscript's URLRequest/Loader classes?

I am trying to get my Actionscript program to turn in a key-value (more specifically: a key, value1, value2) object into a server. I am doing the html request properly, but I'm not sure what kind of object to send; can anyone help?
--Thanks
When I understand correctly you want to post an object to the server, which has 1 key and multiple values. The latter is not supported by a HTTP_POST/GET call, you could however use something as AMF to send a VO from Flash over to a server and decode the AMF object on the server. Not sure whether or not that is where you are looking for, or you just want to post 2 values to a server.
Posting 2 values to a server is simple, but it requires two keys.
public function URLVariablesExample() {
var url:String = "http://www.domain.com/script.php";
var request:URLRequest = new URLRequest(url);
var variables:URLVariables = new URLVariables();
variables.keyOne = new Date().getTime();
variables.keyTwo = "Something";
request.data = variables;
navigateToURL(request);
}

Resources