I'm trying to upload a "binary file" from my cordova app into the imgur.com REST API using AngularJS.
But I'm not sure how I should format my request and what the "binary file" format is. From what I understand, a binary file is any thing that is not plain-text.
This is how I get file data from the local file system. It returns a blob instance. This part works fine.
var request = {
'url': 'file:///my/path/tmp/cdv_photo_025.png',
'options': {
'responseType': 'blob' // also tried 'arraybuffer', no diff.
}
}
var handler = function(response) {
window.myBlobData = response.data;
// handles the response
}
$http.get(request.url, request.options).then(handler);
Then later, I am using this to upload the blob to imgur, but this not working. I get a 400 (Bad Request). I am assuming the content-type, the disabling of the automatic encoding AngularJS does with transformRequest, and of course the binary data:
var request = {
'url': 'https://api.imgur.com/3/upload',
'options': {
'headers': {
'Authorization': 'Client-ID ***********',
'Content-Type': 'application/octet-stream',
},
'transformRequest': []
},
'data': {
'image': window.myBlobData, // a binary file
'type': 'image/png'
}
}
var handler = function(response) {
// handles the response
}
$http.post(request.url, request.data, request.options).then(handler);
For an image upload request with Imgur's API, you can't simply include the Client-ID in the header. If you were instead requesting public read-only information, then that would've been adequate.
If you haven't already, refer to the section on authorization in the Imgur API documentation.
Basically, you'll need to obtain an access token for the user for which you'll be uploading images on behalf of. As suggested by the documentation, you'll want to redirect or open a pop-up window to the below URL.
https://api.imgur.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=access_token
Then, once you've obtained the access token, ensure your upload requests use it:
'Authorization': 'Bearer access_token'
Furthermore, simply use your blob data as the argument for the request (i.e. don't wrap it with the image and type properties).
Related
Have a client-side server in react that need to request files from backend server (kotlin + spring boot) to download them.
Using the request endpoint in Swagger, Postman and Insomnia, i can success download any file with any size.
In my client-side server, have a list of this files that download can be triggered by a click in an icon. I can download files that has less than 10mb with no error, but when file has more than 10mb, it fails with Failed to fetch error.
Actually, it's a weird behavior. Let say i have a file named FILE A that has under than 10mb and FILE B with 25MB (is the max size allowed to upload). In first entried of the page, if i first request to download FILE B, it throw Failed to fetch. Now, if first request is in FILE A and after FILE B, FILE B download is successed. I'm really confused what is going on here.
Code:
const options = {
method: 'GET',
headers: { "Authorization": `Bearer ${user?.token}` },
};
fetch(`http://localhost:8080/storage/download?fileName=${filePath}`, options)
.then(function (response) {
return response.blob();
})
.then(function (myBlob) {
setSpinControl(false);
const file = new Blob(
[myBlob],
{ type: 'application/pdf' }
);
const fileURL = URL.createObjectURL(file);
if (window) {
window.open(fileURL, '_blank');
}
})
.catch((err) => {
setSpinControl(false);
console.log(err)
});
Already tried some alternatives:
Using axios (throw Network Error);
Using libraries as file-saver;
Setting timeout to 9999999;
All achieve same behavior.
I read too that createObjectURL uses memory to perform download, but max size of a file is validated to be 25MB.
Some print of Network tab:
Request Header:
Request Response:
Network List:
Any tips what i can do here?
I have an Angular 1.x application that is expecting to receive a binary file download (pdf) using a $http.post() call. The problem is, I'd like to alternatively get a processing error message that's sent as json. I can do this with the config
headers: {
'Accept': 'application/pdf, application/json'
}
The problem is I have have to set responseType: 'arraybuffer', otherwise the pdf binary is escaped (or altered such that it doesn't load). However, that prevents the json from being read or interpreted correctly.
How can I have both?
Edit: I'm going to try to clarify; perhaps my understanding is incorrect.
$http({
method: 'POST',
url: "/myresource",
headers: {
'Accept': 'application/pdf, application/json'
},
responseType: 'arraybuffer'
})
.then(
function(response) {
// handle pdf download via `new Blob([data])`
}, function(response) {
// pop up a message based on response.data
}
)
In a scenario where I return a pdf data block and a http status of 200, the first function handles the response and prompts the user to save the file. However if the status is an error (422), the response.data is undefined. I assume this is because the responseType is preventing the json from being handled correctly.
If I remove the responseType line, the error data is correctly read, but when the pdf is saved, some of the file bytes aren't correct and it's effectively corrupted. I assume this is because the file is being encoded because javascript was expecting a string.
An XHR responseType property can not be changed after a response has been loaded. But an arraybuffer can be decoded and parsed depending on Content-Type:
var config = {
responseType: "arraybuffer",
transformResponse: jsonBufferToObject,
};
function jsonBufferToObject (data, headersGetter, status) {
var type = headersGetter("Content-Type");
if (!type.startsWith("application/json")) {
return data;
};
var decoder = new TextDecoder("utf-8");
var domString = decoder.decode(data);
var json = JSON.parse(domString);
return json;
};
$http.get(url, config);
The above example sets the XHR to return an arraybuffer and uses a transformResponse function to detect Content-Type: application/json and convert it if necessary.
The DEMO on PLNKR
i have this snippet:
var req = {
method: 'POST',
url: 'https://api.dropboxapi.com/oauth2/token',
headers: {'Content-Type': 'application/json'},
data: {
'code': authCode,
'grant_type': 'authorization_code',
'client_id': 'my_id',
'client_secret': 'my_secret',
'redirect_uri':'http://localhost%3A8080/main'
}
};
return $http(req).then(function(response){
console.log(response.status);
return response;
}, function(err){
console.log(err);
});
The can always ends up in a "bad request" because ""No auth function available for given request""
The same data works with tools to send REST requests... so I don't know what I'm missing here...
Can some help?
The error message indicates that the API didn't receive the expected parameters, or at least not in a format it expected. The documentation for /1/oauth2/token say:
Calls to /oauth2/token need to be authenticated using the apps's key and secret. These can either be passed as POST parameters (see parameters below) or via HTTP basic authentication. If basic authentication is used, the app key should be provided as the username, and the app secret should be provided as the password.
You seem to be attempting to supply the parameters as JSON though, according to your Content-Type header. Try sending them as POST parameters instead.
I have an AngularJS application that sends a POST request to a REST service (onClick method of a button). The POST request contains a JSON object with various settings. The REST service uses those settings to create a MS Word/Excel file.
At the moment the REST service sends the contents of the file back as a byte stream (in response to the previously mentioned POST request). When the file arrives I want a save-file-dialog to show up, where I can save the file. The backend is a Spring Boot app using Spring-MVC.
Can this be done in AngularJS?
If you can't use something like location.href to get your data to the server instead of post it, then check it out others using html 5:
more info AngularJS $http-post - convert binary to excel file and download
$http({
url: 'your/webservice',
method: 'POST',
responseType: 'arraybuffer',
data: json, //this is your json data string
headers: {
'Content-type': 'application/json',
'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
}
}).success(function(data){
var blob = new Blob([data], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
saveAs(blob, 'File_Name_With_Some_Unique_Id_Time' + '.xlsx');
}).error(function(){
//Some error log
});
This is the Controller function I ended up using:
#RequestMapping(value = "/downloadDocx", method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.CREATED)
public void downloadDocx(#RequestBody DocxInputBean docxInput,
HttpServletResponse response) throws Exception {
File docxFile = outputManager.createDocxProfile(docxInput);
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
InputStream is = new FileInputStream(docxFile);
org.apache.commons.io.IOUtils.copy(is, response.getOutputStream());
response.flushBuffer();
is.close();
}
I am facing a weird issue. I am running my angularjs app in nodejs server locally which calls a POST API from my app located on Google App Engine. The API is configured with all CORS headers required as follows:
def post(self):
self.response.headers.add_header("Access-Control-Allow-Origin", "*")
self.response.headers.add_header("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE,OPTIONS")
self.response.headers.add_header("Access-Control-Allow-Headers", "X-Requested-With, content-type, accept, myapp-domain")
self.response.headers["Content-Type"] = “application/json; charset=utf-8”
GET requests to the API work without issues.
POST requests to the API work but ONLY when I send the post data as a 'string of params' and NOT when post data is sent as an object which is the right way to do. Eventually I need to be able to upload pictures using this API so the first solution below might not work for me. Please help!
METHOD 1: This works:
postMessageAPI = "https://myapp-qa.appspot.com/message";
var postData = "conversationid=1c34b4f2&userid=67e80bf6&content='Hello champs! - Web App'";
var postConfig = {
headers: {
"MYAPP-DOMAIN" : "myapp.bz",
'Content-Type': 'application/json; charset=UTF-8'
}
};
$http.post(postMessageAPI, postData, postConfig).
success(function(data){
$log.log("POST Message API success");
}).
error(function(data, status) {
$log.error("POST Message API FAILED. Status: "+status);
$log.error(JSON.stringify(postData));
});
METHOD 2: This fails:
postMessageAPI = "https://myapp-qa.appspot.com/message";
var postData = ({
'conversationid' : '1c34b4f2',
'userid' : '67e80bf6',
'content' : 'Hello champs! - Web App'
});
var postConfig = {
headers: {
"MYAPP-DOMAIN" : "myapp.bz"
'Content-Type': 'application/json; charset=UTF-8'
}
};
$http.post(postMessageAPI, postData, postConfig).
success(function(data){
$log.log("POST Message API success");
}).
error(function(data, status) {
$log.error("POST Message API FAILED. Status: "+status);
$log.error(JSON.stringify(postData));
});
When I use METHOD 2 it fails with the following error in the console:
XMLHttpRequest cannot load https://myapp-qa.appspot.com/message.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://0.0.0.0:8000' is therefore not allowed access.
Please let me know if you have any solution. Thanks in advance.
The issue is most likely with Angular sending a pre-flight OPTIONS request to check the access headers from the server. I am not sure how OPTIONS requests are handled in your API, but I am betting these headers are not being added. I suggest installing Fiddler to monitor the actual requests to see what is going on with the headers. You may only be adding them to your POST responses.
See this answer for details on why METHOD 1 may work in this scenario, while METHOD 2 does not.
Here are some more details about pre-flight requests.