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

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.

Related

Using Oauth 2.0 Token to Upload File to Azure Blob Storage

I want to upload (and eventually, download) a file to Azure Blob Storage. I am not allowed to use the Storage SDK, and can only use preloaded libraries (aka requests, json, etc).
So far, I am able to get the token using this method. Once that is done, I build another requests with the required headers. However, I keep getting a 401 Error.
Code to get Token:
# Set the request url
url = f'https://login.microsoftonline.com/{tenant}/oauth2/token'
# Set the request headers
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
resource = 'https://management.azure.com/'
# Set the request body
body = {
'grant_type': 'client_credentials',
'client_id': client_id,
'client_secret': client_secret,
'resource': resource
}
# Make the POST request to the authentication endpoint
response = requests.post(url, headers=headers, data=body)
# Parse the JSON response
response_json = json.loads(response.text)
# Save to variable
oauth_token = response_json['access_token']
Code works, and return a token. Also, to clarify, I am able to upload a file using the SDK library (which means the app registration and permissions are fine), but not manually. PUT request to Azure Blob Storage is as follows:
# Code to upload file to blob storage
# Set the request url
endpoint = f'https://{storage_account_name}.blob.core.windows.net/{container_name}/{blob_name}'
# Open the file to be uploaded
with open(blob_name, "rb") as f:
# Get file size
file_size = str(len(f.read()))
# Get date
now = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
# Move the pointer back to the beginning of the file
f.seek(0)
# Set the headers for the request
headers = {
"Authorization": f"Bearer {oauth_token}",
"x-ms-version":"2020-04-08",
"Content-Length": file_size,
"x-ms-blob-type": "BlockBlob",
"Date": now
}
# Send the PUT request to upload the file
response = requests.put(endpoint, headers=headers, data=f)
response.status_code
Response Status is 401.
Any help is appreciated.
I've tried filling in more than the required header fields. I've deleted and recreated the app registration. Still unable to access the resource.
For those that might be wondering, I should have specified the resource variable as https://<storage_account_name>.blob.core.windows.net. Code now works.

Spotify API demanding a parameter that is not required

I know this is a particular issue that I'm having but I'm struggling with it for ages...If anyone could give me a hint on how to proceed It would be great.
I'm trying to integrate my flutter app to Spotify API and according to Spotify docs, after the user accepted the conditions at their site, it would be redirected back with a code - In my application I'm already retrieving this code. After that, I need to make a request for another endpoint to "trade" my code for an auth.
The docs demand a POST with 3 parameters(grant_type,redirect_uri and code). But even though I send all of this informations I get a (400 -Bad Request) with the description:
{"error":"invalid_request","error_description":"code_verifier
required"}
But note that code_verifier is only required for a PKCE request, as far as I understand.
My post method is the following:
Future getUserAccessToken(String authorizationCode) async {
// Get the Access Token in exchange for the Authorization Code
var url = 'https://accounts.spotify.com/api/token';
var body = {
'grant_type': 'authorization_code',
'code': authorizationCode,
'redirect_uri': redirectUrl,
};
var response = await post(Uri.parse(url), body: body, headers: {
'Content-Type': "application/x-www-form-urlencoded",
'Authorization':
'Basic ${base64Encode(utf8.encode('$clientID:$clientSecret'))}',
});
}
They also demand this at the authorization header:
Base 64 encoded string that contains the client ID and client secret key. The field must have the format: Authorization: Basic
I would recommend going forward with PKCE. It will protect your app against CSRF and authorization code injection attacks. It's also not too bad to implement.
All you should have to do is:
Generate a random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long. This is your code_verifier
Hash that string with SHA256. This is your code_challenge.
Send those values in the requests as the documentation indicates, which means base64 url encoding them, as well. The Spotify doc says "The body of this POST request must contain the following parameters encoded in application/x-www-form-urlencoded".
Also, in case you haven't seen it, there's a OAuth flow example implemented in JS here: https://github.com/spotify/web-api-auth-examples/blob/master/authorization_code/app.js Their copy of this portion looks like:
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
form: {
code: code,
redirect_uri: redirect_uri,
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64'))
},
json: true
};
request.post(authOptions, function(error, response, body) {...})
The example has "request": "~2.83.0" in the package.json.
Notice how they use form which I would assume is base 64 url encoding the body, as the docs indicate. Not sure how the post method you're using with dart/flutter works with respect to base 64 url encoding, so that's something to investigate, as well.
Credit to https://www.oauth.com/oauth2-servers/pkce/authorization-request/ for providing some of the text in this response

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 a file in SAPUI5 and send to Gateway server

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.

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.