AngularJS: PUT binary data from ArrayBuffer to the server - javascript

Ok, so I try to read a PDF file like this:
reader.readAsArrayBuffer(file);
and then try to send it to the server using $http like this:
$http.put(url, data, {headers: {'Content-Type': 'application/pdf'}});
So, just read & send the binary to the server in raw form. According to some resources I found, passing an ArrayBuffer to XHR should work, but passing it as data to $http just results in a request body like this: {} and Content-Length=2
Reading the file readAsBinaryString() results in a corrupted file (and is apparently deprecated for that very reason)
The use case seems so trivial to me, am I missing something?
Chrome 36, Angular 1.2.20

You have to use reader.readAsArrayBuffer(file); then in the onload callback create an ArrayBufferView from the result:
new Uint8Array(reader.result)
pass that data to $http and overwrite the transformRequest property so angularjs doesn't encode your array into json:
reader.onload = function() {
$http({
method: 'PUT',
headers: {'Content-Type': 'application/pdf'},
data: new Uint8Array(reader.result),
transformRequest: []
})
};
reader.readAsArrayBuffer(file);

Is it because you are just handing the $http method the array buffer instead of writing that buffer into a byte array? If so what you are posting to the server is probably just the arraybuffer object.
Check this post on how to write ArrayBuffer to byte array:
How do I read binary data to a byte array in Javascript?

There are two problems in your request.
You need to supply a data view to $http function.
So, data should be new DataView(data) or new Uint8Array(data) etc
$http always attempt to send the data as json. You can prevent this if you override the transform function. The transform function is the agent that is responsible for transforming your binary into json.
So, you should add transformRequest: [] property to your request.
Example:
var request = $http({
method: 'PUT',
url: 'example.com',
data: new Uint8Array(data),
headers: {'Content-Type': 'application/octet-stream'},
transformRequest: [],
responseType: 'arraybuffer'
});

Related

How do I sent additional data to javascript's fetch() function

I want to use fetch() to query an API endpoint which powers my search page. It returns a list of search results in JSON format.
I also want to pass to the API the current query submitted by the user. The old implementation used jquery and getJSON. Looking at the docs for getJSON, it says that I can pass in a data variable:
data
Type: PlainObject or String
A plain object or string that is sent to the server with the request.
Looking at the docs for fetch, I'm not sure how to pass in data as part of my request. So my question is, how do I pass in a string that will be sent to the server along with my request?
EDIT: I think I can append the query to the request URL, like "/search/?q=Something" and send that. Does anyone have a better way?
If you look at the Body section of the documentation on fetch, it lists several types of values you can use to specify the data sent in your request.
Example using FormData:
var fd = new FormData();
fd.append('q', 'Something');
fetch('/search', {
method : "POST",
body : fd
})
.then(...)
Note that you can't use the body option on GET or HEAD requests (which it seems you may be doing in your case). In that situation, you can build up the parameters using URLSearchParams:
var params = new URLSearchParams();
params.append('q', 'Something');
fetch('/search/?' + params.toString(), {
method: 'GET'
})
.then(...);
You can pass as below
fetch('/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Hubot',
login: 'hubot',
})
})

Angular + PHP: Simplest way to POST an object?

I'm studying php and angular. Currently exploring the possibilities to send data to server side using $http service. This is what I came up with and it seem to work, but it doesn't look elegant.
Angular code:
$http({
method: 'POST',
url: 'server.php',
data: "newUser=" + JSON.stringify(user),
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
} // set the headers so angular passing info as form data (not request payload)
})
.success(function (respose) {
var x = JSON.parse(respose);
console.log(JSON.parse(x));
}).error(function (err) {
console.log(err);
console.log("some kind of error");
});
This is my php code to receive the data and return it:
if (isset($_POST["newUser"])) {
$newUser = $_POST["newUser"];
echo json_encode($newUser);
}
Why do I have to specify the name of the json I'm passing? I mean the newUser prefix under the data of the request.
Secondly, why do I have to json.parse twice the response in order to convert it back to a JS Object?
Why to I have to specify the headers in order to pass a simple JSON string?
Q1. Why do I have to specify the name of the json I'm passing? I mean the newUser prefix under the data of the request.
Q3. Why to I have to specify the headers in order to pass a simple JSON string?
In PHP, you have global arrays like $_POST & $_GET to receive the data extracted from the request that are on the form of key=value.
And $_POST is filled when using application/x-www-form-urlencoded or multipart/form-data as the HTTP Content-Type.
So in order to use these global arrays, you have to full-fill these two conditions
Okay the alternative way is to use php://input stream to directly read the raw input, so you can send the json string directly.
file_get_contents(“php://input”);

unit test receiving binary response with $httpBackend

I have a service that processes a binary response from a web API call:
$http.get('/endpoint', {
params: urlparams,
transformResponse: transform,
responseType: 'arraybuffer'
});
Which I'm trying to test using $httpBackend:
$httpBackend.expectGET('/endpoint').respond(200, data);
But it doesn't work, because data is converted to JSON automatically and I couldn't find a way to specify the responseType in the expectGET call.
How can I make expectGET provide an binary response to the $http.get call? I've tried converting data to an ArrayBuffer to no avail.
I've had a similar issue and used:
$httpBackend.expectGET('/endpoint').respond(200, new Blob([data]));
I however had to update PhantomJs to 2.x from 1.x for this to work.

How to access file uploaded by $http and HTML5 FileReader

I am trying to upload an image inside an Angular app, the code looks like this:
var f = document.getElementById('product-image').files[0],
r = new FileReader();
r.onloadend = function (e) {
var data = e.target.result;
$http({
method: 'POST',
data: data,
url: host + '/uploadFile?' + $rootScope.user._id,
headers: {
'Content-Type': 'multipart/form-data',
'X-File-Name': f.name,
'X-File-Size': f.size,
'X-File-Type': f.type
}
})
.success(function (data, status, headers, config) {
console.log(data)
});
}
r.readAsArrayBuffer(f);
When I have a look at Chrome Header fields of the request, they all look good with values.
Now, how do I access and save that file from the backend endpoint in NodeJS and Express? Do I look inside req variable as usual? Where do I look for the file and its content and meta?
basically, your post request is not 'multipart/form-data', and the data you sent to server is just binary array.
So you could just using NodeJS to save the trunk of binary data to some place.
You need to handle the data different way from normal form upload with files.
If you really want the server to handle the file upload as normal way, you could check the https://github.com/danialfarid/angular-file-upload

Cloudinary Base64 Image uploading in angularjs

$http({method: 'POST',
url: $rootScope.CLOUDINARY_CONFIG.upload_url,
data : {
file : canvasImage,
resource_type : 'image',
format: "jpg",
timestamp : 1375363550,
api_key : $rootScope.CLOUDINARY_CONFIG.api_key,
signature : signature,
public_id : scope.model.public_id
},
headers : {"X-Requested-With": "XMLHttpRequest", "Content-Type" : "multipart/formData"}
}).success(function(data, status, headers, config) {
console.log("success");
}).error(function(data, status, headers, config) {
console.log("fail");
});
I am trying to upload a base64 image to cloudinary account. I have already checked whether the signature, api key, upload url and canvasImage are correct.
Yet whenever the request is sent,
I get an error in response :
{"error":{"message":"Missing required parameter - file"}}
On checking the request payload i can see the file parameter being passed.
The canvasImage is the base64 jpg. of the sort - data:image/jpeg;base64,/9j/4AAQSkZJRgABA.
Can't find anything of this sort in the cloudinary documentation.
Firstly, look into the FormData object. You'll want to use that if you're uploading multi-part form data.
Basically, the FormData object allows you to append files, blobs, and strings (if you attach something that isn't of those three, it will stringify it) using the only function that it has, append i.e:
var newForm = new FormData();
newForm.append('fileName', file);
newForm.append('api_key', $rootScope.CLOUDINARY_CONFIG.api_key);
newForm.append('signature', signature);
newForm.append(public_id, scope.model.public_id);
and so on..
Next.
Set your content-type to undefined instead of multi-part form data. This seems unintuitive, however, what happens is that the browser will automatically set the proper boundary for you and will automatically set the content-type back to multipart/formdata.
Additionally, add a transformRequest to the config set to angular.identity. The browser will try to serialize your form data, therefore you need to stop it from doing so by setting transformRequest to angular.identity.
The overall $http request should look something like this:
$http.post(url, newForm, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(data){
// success
})
.error(function(err){
// log error
})
Also note that FormData is tricky to deal with you because if you console your FormData object, i.e.(console.log(newForm)) all it will show is: FormData {append: function}

Categories

Resources