When trying to download a file using javascript and the google file api referenced here:
https://developers.google.com/drive/api/v3/reference/files
The response body has been encoded as text. The problem is downloading binary files (.sav) which then become encoded incorrectly. The code I'm using is straight forward:
gapi.client.drive.files.get({ 'fileId': self.selectedFileId, alt: 'media' }).then((response) => {
// response.body is text
}
According to Reading a binary file from Google Drive using node.js You can set encoding null but I can't see that anywhere in the current (v3) REST api.
I was having exactly the same issue, had a jpeg file on Drive and it was returning a string: "ÿØÿà\u0000\u0010JFIF\u0000\u0001\u0001\u0000\u0000
looking at the response headers:
"headers": {
"cache-control": "private, max-age=0, must-revalidate",
"content-encoding": "gzip",
"content-type": "text/plain;charset=UTF-8",
"date": "Wed, 24 Feb 2021 20:57:27 GMT",
"expires": "Wed, 24 Feb 2021 20:57:27 GMT",
"server": "UploadServer",
"vary": "Origin, X-Origin",
"x-guploader-uploadid": "ABg5-Uxki1DGxu2guatOLHR3Ixp4HRPxPUvGVk0omTun3qGM_89kO4jYQl9OWhEBpQZW1gUphzmr1mdBtzgKUiOdUgabVBo30A",
"Content-Type": "image/jpeg"
},
you'll see there are two content type headers.... one of which is
"text/plain;charset=UTF-8"
I suspect this is a bug ! After all if I'm requesting the file as binary by using "alt: 'media'" I expect binary not mangled binary as text!
Thanks to #Dave3of5 it now works!
OK so I figured out what I was doing wrong I was converting the string straight into a blob to pass on to my backend I needed to convert to a typed array before converting to a blob:
gapi.client.drive.files.get({ 'fileId': self.selectedFileId, alt: 'media' }).then((response) => {
const charArray = new Array(response.body.length);
for (let i = 0; i < response.body.length; i++) {
charArray[i] = response.body.charCodeAt(i);
}
const typedArray = new Uint8Array(charArray);
const blob = new Blob([typedArray], {type: response.headers['Content-Type']});
Hopefull this helps someone.
Related
We have an API that has multiple different endpoints, as you'd expect. We have the requirement to add a new endpoint which will return an application/pdf along with the file data.
To do this, we return the following:
return {
statusCode: 200,
headers: {
'Content-Type': 'application/pdf',
'Content-disposition': `attachment; filename=${filename}.pdf`,
'Accept': 'application/pdf',
},
body: fileData,
isBase64Encoded: true,
};
The isBase64Encoded only works when a binary media type is set in the API Gateway. As detailed here:
https://medium.com/#ngchiwang/aws-api-gateway-lambda-return-binary-image-ba8faa660839
The issue we have is that by setting the binary media type to * / * (no spaces) on the API Gateway, this, in turn, affects all other endpoints on the API.
Example This breaks one endpoint on the OPTIONS cors check, returning an InternalServerErrorException without reason. This endpoint is just a GET with no data in the request.
Does this mean we need a separate API just for this one endpoint, or is there a way we can include this in the same APIG?
For further clarification, this is a POST that includes a small amount of JSON in the request: {"someValue":1234} and returns the above application/pdf content type.
I'm just tackling this issue and resolved it like this:
Send base 64 string just as normal json response and handle the pdf part on the client
const sendRes = (status:number, body:any) => {
var response = { statusCode: status, headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) };
return response;
};
return sendRes(201, {pdf:your-base64-string} );
Then on the client (Nuxt in my case):
let res = await this.$store.dispatch('pdf/makePdf')
const linkSource = `data:application/pdf;base64,${res.data.pdf}`;
const downloadLink = document.createElement("a");
const fileName = "your-pdf-filename.pdf";
downloadLink.href = linkSource;
downloadLink.download = fileName;
downloadLink.click();
This open a download window and lets you save the file locally
I'm trying to get a multipart streaming post working with HTTPClient for Ruby. Upon which I encounter two problems.
Problem 1:
First, if I try to do a normal post via the way described in the docs; and I return it via httpbin.org
I see this happening:
Code
File.open(path_to_my_file) do |file|
body = [{ 'Content-Type' => 'application/atom+xml; charset=UTF-8',
:content => '<entry>...</entry>' },
{ 'Content-Type' => 'video/mp4',
'Content-Transfer-Encoding' => 'binary',
:content => file }]
res = #http_client.post('http://httpbin.org/post', body: body)
response = res
puts res.body
Result
{
"args": {},
"data": "",
"files": {},
"form": {
"{\"Content-Type\"=>\"application/atom+xml; charset=UTF-8\", :content=>\"<entry>...</entry>\"}": "",
"{\"Content-Type\"=>\"video/mp4\", \"Content-Transfer-Encoding\"=>\"binary\", :content=>#<File:{{path_to_file}}>}": ""
},
"headers": {
"Accept": "*/*",
"Content-Length": "322",
"Content-Type": "application/x-www-form-urlencoded",
"Date": "Mon, 20 May 2019 06:43:17 GMT",
"Host": "httpbin.org",
"User-Agent": "HTTPClient/1.0 (2.8.3, ruby 2.6.0 (2018-12-25))"
},
"json": null,
"origin": "IP, IP",
"url": "https://httpbin.org/post"
}
As you can see, I cannot see the content of the file, but only the identifier containing the URI to the File; and as such, I don't know how to fix it so that I can see and return the content. As far as I know it looks like it tries to see the File object as a String, which is of course not what I'd like it doing.
Problem2:
Whenever I create the body dynamically, that is; creating the hash-array dynamically with objects set in my code, and I try to send it asynchronously (which I believe is the correct way of getting it streaming in HTTPClient for Ruby, although I'm not entirely sure)
it sends the entire body as data, and no longer as either a form or headers.
code
request_body = []
body.files.each do |k, v|
request_body.push( { 'Content-Type' => v.content_type, :content => v.content })
end
body.values.each { |k, v| request_body << { k.to_sym => v }}
#This creates the array correctly, although I just wanted to show how it was generated
connection = #http_client.send(method + '_async', uri, body: request_body, header: headers)
response = connection.pop
# Starts reading result here
Response
"args": {},
"data": "%7B%22Content-Type%22%3D%3E%22video%2Fmp4%22%2C+%3Acontent%3D%3E%23%3CFile%3A%2Fhome%2Fuser%2Ffiles%2file.mp4%3E%7D=&%7B%3Avalue%3D%3E%22hello+world%22%7D=",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Content-Length": "247",
"Content-Type": "application/json",
"Date": "Mon, 20 May 2019 06:44:11 GMT",
"Host": "httpbin.org",
"User-Agent": "HTTPClient/1.0 (2.8.3, ruby 2.6.0 (2018-12-25))",
"ApplicationIdentifier": "identifier"
},
"json": null,
"origin": "IP, IP",
"url": "https://httpbin.org/post"
}
As you can see, it puts everything in data, and I honestly don't know how to get it to send the post with the body as a form instead of as data. I send the file here as a File object.
I have tried sending it as a normal post instead of post_async, but it does not seem to be working.
Has anyone encountered these problems before and know how I should go about fixing these? (or can you see where I went wrong so that I can at least try getting a bit further)
Thanks in advance
Regarding your first issue, it appears that the documentation is not correct. I've performed some tests, and the content type appears to be application/x-www-form-urlencoded. You need to set the content type explicitly:
body: body, header: {'Content-Type': 'multipart/form-data; boundary=ABC'}
That's not sufficient though. You also need to set the content disposition manually. For instance:
body = [{ 'Content-Type' => 'application/atom+xml; charset=UTF-8',
'Content-Disposition' => 'form-data; name="name1"',
:content => '<entry>...</entry>' },
{ 'Content-Type' => 'video/mp4',
'Content-Disposition' => 'form-data; name="file"; filename="myfile.pdf"',
'Content-Transfer-Encoding' => 'binary',
:content => file }]
With this, httpbin reports one file and one form parameter.
Your second issue is related to the same. Both your files and values need to have the content disposition set. For instance (quick and dirty, style can probably be better):
files.each do |k, v|
request_body << { 'Content-Type' => v.content_type, :content => v.content, 'Content-Disposition' => 'form-data; name="' + k + '"; filename="' + v.filename + '"' }
end
values.each { |k, v| request_body << { :content => v, 'Content-Disposition' => 'form-data; name="' + k + '"' }}
Note that you should escape any " in a name or filename.
I'm trying to upload a small image file to Google Drive using RestClient. I already have an access token (being requested in earlier code) but I am not sure how to form the payload.
The API docs state the request should look like this:
POST /upload/drive/v3/files?uploadType=media HTTP/1.1
Host: www.googleapis.com
Content-Type: image/jpeg
Content-Length: number_of_bytes_in_file
Authorization: Bearer your_auth_token
JPEG data
I have tried the following but it results in an error:
require 'rest-client'
file = File.open("./uploaded-by-api.jpg")
response = RestClient::Request.execute(method: :post, url: 'https://www.googleapis.com/upload/drive/v3/files', payload: { uploadType: "media", file: file, }, headers: { Authorization: "Bearer #{access_token}", "Content-Type" => "image/jpeg", "Content-Length" => "1000" })
I'm pretty sure there is a mistake in the way I'm including the actual file data but I can't find any examples of this with RestClient.
Ok solved it with the following:
response = RestClient.post(
'https://www.googleapis.com/upload/drive/v3/files',
{ 'uploadType' => "media", 'upload' => file },
"Authorization" => "Bearer #{access_token}",
"Content-Type" => "image/jpeg",
"Content-Length" => "1000"
)
I am trying to make a POST request using polymer core-ajax to server runnung golang. After a lot of search (because i am new to this stuff) i ended up with the following code. Also, GET request is working perfect. POST parameters i dont understand how to pass using core-ajax.
<polymer-element name="register-user" attributes="url">
<template>
<core-ajax id="ajaxSubmit" url="{{url}}" contentType="application/json" handleAs="json" method="post" on-core-response="{{response}}"></core-ajax>
<style type="text/css">
</style>
</template>
<script>
Polymer({
buttonListener: function() {
var data = '{"Name":"'+ this.name +'", "Email":"'+ this.email +'"}';
this.$.ajaxSubmit.data = data;
this.$.ajaxSubmit.go();
console.log(data);
},
response: function(oldValue){
console.log(this.response);
}
});
</script>
</polymer-element>
above code returns 500 (Internal Server Error) however when i make a POST request using curl i.e
curl -i -H 'Content-Type: application/json' -d '{"Name":"Batman",
"Email":"batman#gmail.com"}' http://so.me.ip.ad:8080/register
it works as it should and returns
HTTP/1.1 200 OK
Content-Type: application/json
X-Powered-By: go-json-rest
Date: Wed, 29 Apr 2015 05:40:15 GMT
Content-Length: 117
{
"id": 3,
"name": "Batman",
"email": "batman#gmail.com",
"createdAt": "2015-04-29T05:40:15.073491143Z"
}
also, i have a CORS middleware set up on server i.e
api.Use(&rest.CorsMiddleware{
RejectNonCorsRequests: false,
OriginValidator: func(origin string, request *rest.Request) bool {
return origin == "http://0.0.0.0:8000"
},
AllowedMethods: []string{"GET", "POST", "PUT"},
AllowedHeaders: []string{
"Accept", "Content-Type", "X-Custom-Header", "Origin"},
AccessControlAllowCredentials: true,
AccessControlMaxAge: 3600,
})
What am i doing wrong? Any feedback will be of great help! Thanks ^.^
Edit : here is a little more info if it can help..
I think CORS is a red herring. The problem may be that you are sending the data form-encoded and not as json. I found a bug from a user with a similar problem.
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
X-Powered-By: go-json-rest
Date: Fri, 12 Dec 2014 04:29:59 GMT
Content-Length: 71
{
"Error": "invalid character '\\'' looking for beginning of value"
}
Perhaps you should use .body instead of .data? See this answer.
From the polymer documentation:
body: Optional raw body content to send when method === "POST".
Example:
<core-ajax method="POST" auto url="http://somesite.com"
body='{"foo":1, "bar":2}'>
</core-ajax>
I am trying to post an image using the source parameter (Multipart/form-data) in the Graph API explorer. My source parameter looks like this:
{Content-Type: multipart/form-data; boundary=xxxsrixxx
--xxxsrixxx
Content-Disposition: attachment; filename=try2.gif
Content-Type: image/gif
Content-Transfer-Encoding: binary,base64
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
--xxxsrixxx--}
This always seems to return
{
"error": {
"message": "(#324) Requires upload file",
"type": "OAuthException",
"code": 324
}
}
Few questions:
Does facebook source parameter accept base64 encoded image data?
Has anyone tried using the source parameter in the graph api explorer and have a working sample?
should the source param value should be url-encoded?
Note: My access token has all the required permissions and if i am not wrong, my encoding seems to be wrong. The base64 encoded text above is a complete image and i could decode it and get the image.
Ok. This is going to sound crazy. After a week of trying I was able to post an image successfully to the facebook wall. The fix was to send the raw binary data as it is (no base64 encoding - fb does not like it) and make sure your multipart/form-data is correct. For instance, if you boundary is defined as "boundary=--xxxsrixxx" your actual request should look like this "----xxxsrixxx\r\nContent-Disposition......\r\n----xxxsrixxx"
This works fine for me, it only uses native JavaScript features and a file input:
const fileReader = new FileReader();
const file = document.getElementById('imageInput').files[0];
fileReader.onloadend = async () => {
const photoData = new Blob([fileReader.result], {type: 'image/jpg'});
const formData = new FormData();
formData.append('access_token', pageAccessToken);
formData.append('source', photoData);
formData.append('message', 'some status message');
let response = await fetch(`https://graph.facebook.com/${pageId}/photos`, {
body: formData,
method: 'post'
});
response = await response.json();
console.log(response);
};
fileReader.readAsArrayBuffer(file);
More information: http://www.devils-heaven.com/facebook-javascript-sdk-photo-upload-with-formdata/