upload a file in SAPUI5 and send to Gateway server - sapui5

I am having difficulties trying to figure out a way to send uploaded file and send it to a Gateway server. I am using the basic FileUploader control.
***<u:FileUploader
id="fileUploader"
name="myFileUpload"
uploadUrl="upload/"
width="400px"
tooltip="Upload your file to the local server"
uploadComplete="handleUploadComplete"/>
<Button
text="Upload File"
press="handleUploadPress"/>***
And in the controller I have the following event handler
***handleUploadPress: function(oEvent) {
var oFileUploader = this.getView().byId("fileUploader");
oFileUploader.upload();
}***
What code should I add after oFileUploader.upload() to have an xstring that I can pass to my attachment property of my OData srvice
Thank you

The first thing to do is to make sure that you have a gateway service that is able to handle media types and streams. To set this up you need to set the entity that is handling the file content as a media type and get the logic in place that deals with streams (CREATE_STREAM). You can find more information on how to do this in this SCN blog.
In your UI5 application, you will have to set the URL of the upload control to e.g. /sap/opu/odata/sap/AWESOME_SERVICE/Customers('0000123456')/$value so that the file is handled by the CREATE_STREAM method that you just implemented.
When the upload is eventually taking place, you need to deal with two header parameters; the slug and the CSRF token. The slug header should be set to e.g. the filename, while the CSRF token needs to be retrieved using a pre-flight request. To set the headers, you could use something like this:
oFileUploader.addHeaderParameter(new FileUploaderParameter({
name: "x-csrf-token",
value: _csrfToken
}));
The slug header parameter could be set in a similar way and should contain something that identifies the file, e.g. filename or id.
To determine the CSRF token, you could do something like this:
var _csrfToken = "";
jQuery.ajax({
url: "/sap/opu/odata/sap/AWESOME_SERVICE",
headers: {
"X-CSRF-Token": "Fetch",
"X-Requested-With": "XMLHttpRequest",
"DataServiceVersion": "2.0"
},
type: "GET",
contentType: "application/json",
dataType: 'json',
success: function(data, textStatus, jqXHR) {
_csrfToken = jqXHR.getResponseHeader('x-csrf-token');
}
});
With the right header parameters in place, sending the file to a properly configured gateway entity should do the trick to get your file uploaded.

Related

Empty MultipartFile[] when sending files from Vue to SpringBoot controller

I'm doing a program that will help me to make monthly reports and I stuck at uploading photos which I need for one kind of the reports. For some reason, it doesn't get an array in the controller.
I use Springboot RestController at the backend and Vue with BootstrapVue and vue-resource on the other side.
index.html (BootstrapVue):
<b-form-file
v-model="photos"
accept="image/*"
multiple
placeholder="..."
></b-form-file>
<b-button #click="uploadPhotos">Upload</b-button>
inside vuemain.js:
data: {
photos: null,
},
methods: {
uploadPhotos(){
var formData = new FormData();
formData.append("photos", this.photos);
this.$http.post('reports/photo', formData).then(result => {
...
})
}, ...
inside Controller:
#PostMapping("/photo")
public void addPhoto(#RequestParam("photos") MultipartFile[] photo) {
System.out.println(photo.length); // shows 0
}
what I see inside Params at browser:
XHRPOSThttp://localhost:8080/reports-maker/reports/photo
[HTTP/1.1 500 326ms]
Request payload
-----------------------------4469196632041005505545240657
Content-Disposition: form-data; name="photos"
[object File],[object File],[object File],[object File]
-----------------------------4469196632041005505545240657--
​
So for some reason at this point #RequestParam("photos") MultipartFile[] photo it's empty array. But if I change it to just one photo like this: #RequestParam("photos") MultipartFile photo and send one from js: formData.append("photos", this.photos[0]); everything works nicely and photo gets uploaded to the server.
It's my first experience with Vue and to be honest I don't want to go deep into JS learning, so probably there is some silly mistake somewhere. Any way I can use a loop in JS method to upload them one by one, but that would be so ugly. I hope there is a better way to do it (without any additional JS libraries of course).
If you use axios then you should add header
var headers = {
'Content-Type': 'multipart/form-data',
};
axios.post(
'reports/photo',
formData,
{
headers: headers,
}
)
...
to be able send files to the server.
I agree, sending files in separate requests one by one is very "ugly", but I also don't like the idea of not using the mapping resources of Spring Boot, having to send all files with different names (seems disorganized) and having to work with MultipartHttpServletRequest, but there is a simple solution for this: Ian's answer to this question (not realy related to Vue.js, but useful) worked for me:
In order for Spring to map items in a request to a list, you need to provide the same name (in the FormData.append calls) for each item when appending to the form data. This allows Spring to effectively see the request as name=value1&name=value2&name=value3 (but obviously in the form of form data). When Spring sees the same key ("name") multiple times, it can map the values into a collection.
In your .vue file, append the photos with the same name:
for (let i = 0; i < this.photos.length; i++) {
formData.append("photos", this.photos[i]);
}
And in your Controller:
#PostMapping("/photo")
public void addPhoto(#RequestParam("photos") MultipartFile[] photo) {
System.out.println(photo.length); // Should be greater than 0 now
}
Note:
I used Vue Axios to consume my API and manually added the Content-Type: multipart/form-data header. Make sure its in your request header list.
I found an acceptable solution here https://stackoverflow.com/a/33921749/11508625 Rossi Robinsion's code works nicely. At least it looks better than sending files in separate requests one by one.
The answer is based on using getFileNames() which helps to iterate on files inside a request even if they are not in the array.

Upload and Name a File Using Google Drive REST API v3 and Angular 2

I'm creating a Google Drive service using the Drive REST Api v3 in Angular 2. Most of the functionality is in place: view file, download, create etc.. but I cannot find how to name a file (either when creating a file or updating).
I'm using the following docs pages: create and update. They say the file name should be part of the request body. The relevant code from my Google Drive service is bellow.
createFile(name :string, content :string) :Promise<Object> {
let headers = new Headers({
'Content-Type': 'text/markdown',
'Authorization': 'Bearer ' + this.token,
'name': name //TODO name not working!
});
let options = new RequestOptions({ headers: headers }); // Create a request option
return this.http
.post('https://www.googleapis.com/upload/drive/v3/files' + '?uploadType=multipart', content, options)
.toPromise();
}
updateFile(id :string, content :string, name :string) :Promise<Object> {
let headers = new Headers({
'Content-Type': 'text/markdown',
'Authorization': 'Bearer ' + this.token,
'id': id,
'name': name //TODO name not working!
}); //generate headers
let options = new RequestOptions({ headers: headers }); // Create a request option
return this.http
.patch('https://www.googleapis.com/upload/drive/v3/files/' + id + '?uploadType=multipart', content, options)
.toPromise();
}
To summarise files are being created and updated fine (including content) but naming and renaming a file doesn't work at all.
Thanks for any help.
Try placing name in the request body and not in the request header as described in the Files: create:
Request body
In the request body, supply a Files resource with the following properties as the metadata. For more information, see the document on media upload.
To test it, try using API Explorer to help you explore various Google APIs interactively.
Sample Request:
POST https://www.googleapis.com/drive/v3/files?key={YOUR_API_KEY}
{
"name": "My File"
}
Response:
200
{
"kind": "drive#file",
"id": "fileID",
"name": "My File"
}
There is also a related SO post that explain how to insert file to Google Drive through API.
Hope this helps.
I also faced that problem. I think there is 3 solutions:
Use multipart upload https://developers.google.com/drive/v3/web/multipart-upload with different headers for file metadata and actual file. Me myself stuck there, didn't found how to add boundaries to separate request headers in Angular 2+
Upload file in two requests. First to create empty file with metadata (response will provide id of the file) and second to actually "update" the file.
Use resumable upload. First request to "setup metadata" (will not even create empty file) and get "special link" where to send request to upload actual file. And this approach have some other features, like uploading in chunks.https://developers.google.com/drive/v3/web/resumable-upload
Here is the link to another Question with implementation of resumable upload in Angular 2 and DRIVE REST API V3
Angular 2+ HTTP POST and GDrive API. Resumable file upload with name
I hope it might be useful.
You are trying to set the name using an http header. This is wrong. I can't begin to understand how you thought that was the way to do it, so you need to go back and reread the Drive API documentation.
In short, the name: "name" should be a JSON object passed in the body of the request, not in an http header.

RESTful from Google Apps Script

I invoke a RESTful webserice via a method GET url with paramters from google apps script successfully using the following:
UrlFetchApp.fetch(url);
But when I switch the method to use POST, the result returned tells that no parameter was sent:
UrlFetchApp.fetch(url, options);
I also tried to use Google Chrome's (Advanced) Rest Client, and the payload parameters are not sent there as well.
What am I missing?
Below is the Google Chrome's (Advanced) Rest Client interface.
You mention in your comments that you are converting the payload you send to JSON.
First, are you certain the remote service is expecting a JSON payload, rather than form encoded data?
If the service expects form encoded data, don't JSON.stringify your payload:
var data = {"x1": "1", "x2": "2"};
var options = {method : "post", payload : data};
If the service does expect a JSON payload, you need to set the content type:
var data = {"x1": "1", "x2": "2"};
data = JSON.stringify(data);
var options = {method : "post", payload : data, contentType:"application/json"};
Otherwise the default content type of 'application/x-www-form-urlencoded' is used.
See the description on "contentType" under "Advanced Parameters": https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app

Can we add Json object to RequestHeader of HTTP GET method

I am developing a new REST-full webservice for our application,I wanted to send the reqest data in requestHeader instead of sending as query param, as my request data is large.
I have My jquery code like below to add json to the request header and call the REST service GET method.
$.ajax({
beforeSend: function(req) {
req.setRequestHeader("test", "{name:mouli, id:918}");},
type : "GET",
data :'',
dataType : "jsonp",
url : 'http://localhost:29801/RestFulJSONExample/rest/jobdesc?callback=?',
success : function(data) {
alert("invoked");
}
});
});
And my GET method in my REST service is like
#GET
#Produces("application/javascript")
public JSONWithPadding getJobDescription(#Context HttpHeaders headers) {
List<String> requestHeader = headers.getRequestHeader("test");
//some logic here.
}
i could able to get the JSON object from the request header which i have added in the jquery request.
My question is ..
Can i follow this approach? is it secure and safe?
If not please tell me the other way?
What appears at the right side of the ":" in a header is mostly free. You have to take into account character set restriction in HTTP, and possible carriage returns in the JSON value (you know, headers of more than one line have a specific syntax). If your JSON examples are relatively simple, then I see no problem in that. It is just another way of organizing the actual value of a header line.

Origin is not allowed by Access-Control-Allow-Origin for HTTP DELETE

I currently playing around with the Facebook JavaScript SDK and the Scores API ( https://developers.facebook.com/docs/score/ ). I wrote a small application to save (post) scores and now I want to delete scores. Posting (saving) them works fine.
My code looks like this:
var deleteHighScoreUrl = 'https://graph.facebook.com/'+facebook.user.id+'/scores?access_token='+facebook.application.id+'|'+facebook.application.secret;
jQuery.ajax(
{
type: 'DELETE',
async: false,
url: deleteHighScoreUrl,
success: function(data, textStatus, jqXHR)
{
console.log('Score deleted.');
}
});
The "facebook" variable is an object that holds my application data. For HTTP POST it works fine but for HTTP DELETE I get the response "NetworkError: 400 Bad Request" in Firebug (with Firefox 10). I saw that Firefox first sends an HTTP OPTIONS (to see if it is allowed to use HTTP DELETE) which leads to this error so I tried the same thing with Google Chrome. Google Chrome sends a real HTTP DELETE which then returns:
"XMLHttpRequest cannot load
https://graph.facebook.com/USER_ID/scores?access_token=APP_ID|APP_SECRET.
Origin MY_DOMAIN is not allowed by Access-Control-Allow-Origin".
I think that this is a classical cross domain issue but how to solve it? I've added my domain to my facebook application (at https://developers.facebook.com/apps) and Facebook has a paragraph which is called "Delete scores for a user". So it must be possible to delete the scores (somehow)?
Because of Cross-Site-Scripting (XSS) a HTTP DELETE is not possible. But you can send a HTTP POST request with the query parameter ?method=delete, which then deletes the score.
Code Sample:
Facebook.prototype.deleteUsersHighScore = function()
{
var deleteHighScoreUrl = 'https://graph.facebook.com/'+this.user.id+'/scores?access_token='+this.application.id+'|'+this.application.secret+'&method=delete';
jQuery.ajax(
{
type: 'POST',
async: false,
url: deleteHighScoreUrl,
success: function(data, textStatus, jqXHR)
{
console.log('Score deleted.');
}
});
}
This is the Cross Domain security issue.
The fact that your error contains the message "Origin MY_DOMAIN" would tell me that somewhere in your code you have copied one of Facebook's examples but not changed the value for MY_DOMAIN to the correct domain you are using.
I would check all of your code for the value "MY_DOMAIN".
Please ignore this advice if you have changed the value to hide your actual domain in your question.