Posting to Microsoft Cognitive Face API using Ionic - AngularJS - angularjs

Currently I am using Ionic to develop a Facial Recognition app.
I am using Microsoft's Face API for the same.
My problem is I keep getting Error 400: Decoding error, image format unsupported.
After doing some research, I ran into this link
Quoting START,
When you're submitting a binary payload, you should not base64 encode it. Here's what you might do instead..
URI uri = builder.build();
HttpPost request = new HttpPost(uri);
request.setHeader("Ocp-Apim-Subscription-Key", "...");
File file = new File("...");
FileEntity reqEntity = new FileEntity(file, ContentType.APPLICATION_OCTET_STREAM);
request.setEntity(reqEntity);
Quoting END
With Ionic/AngularJS, the captured image using Device Camera or selected from Image Gallery is returned in the form of base64 string. So I looked for ways to decode the data into Binary using this gist but still failed.
Here's my controller code where I make the API call.
$scope.postToAPI = function () {
// make http call to MS cognitive API
var formData = new FormData();
formData.append('file', $scope.picture);
$http.post($scope.cognitiveServices.endpoint, formData, {
headers: { 'Content-Type': 'application/octet-stream', 'Ocp-Apim-Subscription-Key':$scope.cognitiveServices.apiKey }
}).success(function (data) {
console.log(JSON.stringify(data));
alert("Awesome");
}).error(function (err) {
alert("Fail ->" + err.code + " " + err.message);
console.log("Some error occured");
});
}
I have
tried setting transform:angular.identity and Content-Type: undefined
tried sending image without Form Data (just a random trial)
tried converting base64 string to Uint8 Array (by following the solution to this SO question)
All my attempts so far have failed. Can someone explain what I am doing wrong?
P.S: This is my first time with Ionic framework.

Related

Unable to download XLSX workbook without errors Springboot / React

I have an application that is a Springboot backend and a reactJs frontend. I am able to successfully download an excel workbook on my localhost through the application as well as in postman without any errors, but when I test the app on the server I get an error that the file format or extension is not valid.
Also when I test the apps actual url using postman I get a 200 response but nothing is actually returned.
I am using axios and read over this post.
On the backend, the excel workbook is being put together using Apache POI XSSFworkbook.
The backend response entity looks like this
return ResponseEntity
.ok()
.contentType(MediaType.valueOf("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + params.getDeqId() + "Data.xlsx").body(workbook::write);
On the react side, I've tried a few different things. Currently I have:
return axios
.post(`/api/excelexport/watersystemexcel`, submittedData, { responseType: "arraybuffer" })
.then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", submittedData.deqId + "Data.xlsx");
document.body.appendChild(link);
link.click();
this.setState(() => ({ downloading: false }));
});
I have also tried formating the axios request like:
axios.post(`/api/excelexport/watersystemexcel`, submittedData, {
responseType: "arraybuffer",
headers: { "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }
})
but this results in a 415 error, saying that that specified content-type isn't acceptable.
Lastly, I have also tried changing the file extension to .xls rather then .xlsx on both the front and backend. When I do this the workbook does download but when the file is opened there is an error that file extensions don't match, and that the file may be corrupt.
return ResponseEntity
.ok()
.contentType(MediaType.valueOf("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + params.getDeqId() + "Data.xlsx")
.body(workbook::write);
When you build your own ResponseEntity object, you have not specified the Content Length, so it could be problematic, and maybe that's why you get a corrupted file at the end.
That's how I do when I need to display images or videos :
return ResponseEntity.status(HttpStatus.OK).contentLength(cr.contentLength()).body(cr);
where cr is a FileSystemResource or a ClasspathResource or whatever Resource you need to send as a HTTP Response Entity.
It seems to be identical, but not, look at the end of my code with the contentLength part before the body part.

How to send a local image instead of URL to Computer Vision API using React

I would like to upload local image file and extract text from it. I followed the below link and it works as expected when I pass URL. https://learn.microsoft.com/en-us/azure/developer/javascript/tutorial/static-web-app/add-computer-vision-react-app
I managed to configure for local image and get the base64 encoded dataURL of the uploaded image. But when I pass base64 encoded dataURL to Computer Vision API , it says "Input data is not a valid image" (POST 400 status code). I am getting error in the line that is shown below:
const analysis = await computerVisionClient.analyzeImage(urlToAnalyze, { visualFeatures });
The code I have included for handling local image:
const handleChange = (e) => {
var file = e.target.files[0];
var reader = new FileReader();
reader.onloadend = function()
{
setFileSelected(reader.result) // this is the base64 encoded dataurl
}
reader.readAsDataURL(file);
}
In computerVision.js file, I have changed the 'contentType' in header as below.
const computerVisionClient = new ComputerVisionClient(
new ApiKeyCredentials({ inHeader: {'Ocp-Apim-Subscription-Key': key, 'Content-Type': 'application/octet-stream'} }), endpoint);
I tried replacing client.read() with readTextInStream() as per docs in computerVision.js (please refer above link), but still throws error.
May I know why I get the error "Input data is not a valid image" ? Thanks.
Here is the link for input requirements.
There is a brand new online portal provided by Microsoft https://preview.vision.azure.com/demo/OCR
The advantage is that it will directly list your available resources so you just have to pick the right one, then you test, and there are also some samples.

Uploading file to openstack object storage from JavaScript

I have a openstack object storage container to which I'm trying to upload files directly from browser.
As per the documentation here, I can upload the file using a PUT request and I'm doing this using Angularjs provided $http.put method as shown below.
$http.put(temporaryUploadUrl,
formData,
{
headers: {
'Content-Type': undefined
}
}
).then(function (res) {
console.log("Success");
});
The file uploads successfully and it has no problems in authentication and gives me a 201 Created response. However the file is now containing junk lines on the top and bottom of it because its a multipart request sent using FormData().
Sample file content before upload:
Some sample text
here is more text
here is some other text
File content after downloadiong back from openstack container :
------WebKitFormBoundaryTEeQZVW5hNSWtIqS
Content-Disposition: form-data; name="file"; filename="c.txt"
Content-Type: text/plain
Some sample text
here is more text
here is some other text
------WebKitFormBoundaryTEeQZVW5hNSWtIqS--
I tried the FileReader to read the selected file as a binary string and wrote the content to the request body instead of FormData and the request which works fine for text files but not the binary files like XLSX or PDF The data is entirely corrupted this way.
EDIT:
The following answer is now considered a less performing workaround As
it will encode the entire file to base64 multipart form data. I would
suggest go ahead with #georgeawg's Answer if you are not Looking for a
formData + POST solution
Openstack also provides a different approach using FormData for uploading one or more files in a single go as mentioned in this documentation. Funny this was never visible in google search.
Here is a brief of it.
First you need to generate a signature similar to tempUrl signature using the following python procedure.
import hmac
from hashlib import sha1
from time import time
path = '/v1/my_account/container/object_prefix'
redirect = 'https://myserver.com/some-page'
max_file_size = 104857600
max_file_count = 1
expires = 1503124957
key = 'mySecretKey'
hmac_body = '%s\n%s\n%s\n%s\n%s' % (path, redirect,
max_file_size, max_file_count, expires)
signature = hmac.new(key, hmac_body, sha1).hexdigest()
Then in your javascript call post to the container like this.
var formData = new FormData();
formData.append("max_file_size", '104857600');
formData.append("max_file_count", '1');
formData.append("expires", '1503124957');
formData.append("signature", signature);
formData.append("redirect", redirect);
formData.append("file",fileObject);
$http.post(
"https://www.example.com/v1/my_account/container/object_prefix",
formData,
{
headers: {'Content-Type': undefined},
transformRequest: angular.identity
}
).then(function (res) {
console.log(response);
});
Points to note.
The formData in POST request should contain only these
parameters.
The file entry in the formData should be the last one.(Not sure why
it doesnt work the other way around).
The formData content like path with prefix, epoch time, max file
size, max file count and the redirection urls should be the same as
the one which were used to generate the signature. Otherwise you will
get a 401 Unauthorized.
I tried the FileReader to read the selected file as a binary string and wrote the content to the request body instead of FormData and the request which works fine for text files but not the binary files like XLSX or PDF The data is entirely corrupted this way.
The default operation for the $http service is to use Content-Type: application/json and to transform objects to JSON strings. For files from a FileList, the defaults need to be overridden:
var config = { headers: {'Content-Type': undefined} };
$http.put(url, fileList[0], config)
.then(function(response) {
console.log("Success");
}).catch(function(response) {
console.log("Error: ", response.status);
throw response;
});
By setting Content-Type: undefined, the XHR send method will automatically set the content type header appropriately.
Be aware that the base64 encoding of 'Content-Type': multipart/form-data adds 33% extra overhead. It is more efficient to send Blobs and File objects directly.
Sending binary data as binary strings, will corrupt the data because the XHR API converts strings from DOMSTRING (UTF-16) to UTF-8. Avoid binary strings as they are non-standard and obsolete.

Error 400 when POST'ing JSON in angularjs + Spark Single Page Application

I'm new to Single Page Application area and I try to develop app using angularjs and Spark framework. I get error 400 bad request when I want to post JSON from my website. Here is code fragment from client side:
app.controller('PostTripCtrl', function($scope, $http) {
$scope.newTrip = {};
$scope.submitForm = function() {
$http({
method : 'POST',
url : 'http://localhost:4567/trips/add',
data : $scope.newTrip,
headers : {
'Content-Type' : 'application/x-www-form-urlencoded'
}
}).success(function(data) {
console.log("ok");
}).error(function(data) {
console.log("error");
console.log($scope.newTrip);
});
};
});
Values that are to be assigned to newTrip are read from appropriate inputs in html file. Here is server-side fragment:
post("/trips/add", (req, res) -> {
String tripOwner = req.queryParams("tripOwner");
String startDate = req.queryParams("startDate");
String startingPlace = req.queryParams("startingPlace");
String tripDestination = req.queryParams("tripDestination");
int tripPrice = Integer.parseInt(req.queryParams("tripPrice"));
int maxNumberOfSeats = Integer.parseInt(req.queryParams("maxNumberOfSeats"));
int seatsAlreadyOccupied = Integer.parseInt(req.queryParams("seatsAlreadyOccupied"));
tripService.createTrip(tripOwner, startDate, startingPlace, tripDestination, tripPrice, maxNumberOfSeats,
seatsAlreadyOccupied);
res.status(201);
return null;
} , json());
At the end I obtain error 400 bad request. It is strange for me that when I want to see output on the console
System.out.println(req.queryParams());
I get json array of objects with values written by me on the website. However, when I want to see such output
System.out.println(req.queryParams("tripOwner"));
I get null. Does anyone have idea what is wrong here?
I think the main problem is that you are sending data to your Spark webservice with the 'Content-Type' : 'application/x-www-form-urlencoded' header. Try sending it as 'Content-Type' : 'application/json' instead, then in your Java code declare a String to receive req.body(), you'll see all your data in there.
Note: When you try to acces your data like this req.queryParams("tripOwner"); you're not accessing post data, but you're seeking for a get parameter called tripOwner, one that could be sent like this http://localhost:8080/trips/add?tripOwner=MyValue.
I would advise using postman to post a request to your server and see if it works. Try a different content type too. Try using curl and play with the various headers you are sending. 400 suggests the wrong data is being sent or expected data is missing or the data is the wrong type but based on your code you've provided I can see nothing wrong (but see below).
When your server receives a request log all request headers being received and see what changing them does. If it works in postman then you can change your client code to mirror the headers postman is using.
Does your spark server validate the data being sent before your controller code is hit? If so ensure you are adhering to all validation rules
Also on looking at your code again your client is sending the data in the post data but your server is expecting the data in the query string and not in the post data?
What happens if your server just sends a 201 response and does nothing else? Does your client get a 201 back? If so it suggests the hook up is working but there is something wrong with the code before you return a 201, build it up slowly to fix this.
Ok, I managed to cope with that using another approach. I used Jackson and ObjectMapper according to Spark documentantion. Thanks for your answers.
You can see more about that here: https://sparktutorials.github.io/2015/04/03/spark-lombok-jackson-reduce-boilerplate.html
You're probably just needed to enable CORS(Cross-origin resource sharing) in your Spark Server, which would have allowed you to access the REST resources outside the original domain of the request.
Spark.options("/*", (request,response)->{
String accessControlRequestHeaders = request.headers("Access-Control-Request-Headers");
if (accessControlRequestHeaders != null) {
response.header("Access-Control-Allow-Headers", accessControlRequestHeaders);
}
String accessControlRequestMethod = request.headers("Access-Control-Request-Method");
if(accessControlRequestMethod != null){
response.header("Access-Control-Allow-Methods", accessControlRequestMethod);
}
return "OK";
});
Spark.before((request,response)->{
response.header("Access-Control-Allow-Origin", "*");
});
Read more about pre-flighted requests here.

Authendicated video playback using angularjs

I'm working on an angularjs SPA type project and there I came across a situation where i need to play an ordinary mp4 file, but the issue is that video is not public so in order to access it i need to send an access token in header.
so my question is
If this is possible how do i do the playback
is token based authentication is a right approach to securely access a media
At the end I came up with this code, code you see is a part of a directive
var video = $http({
method : "GET",
url : "http://path/small1.mp4",
responseType : "blob",
headers : { X-Access-Token : "token" }
});
video.success(function(data,status,headers,config){
if((data != undefined)){
var dataURL= window.URL.createObjectURL(data);
$scope.videourl = dataURL;
//window.location =dataURL;
var video = document.getElementById("emptyvideo");
video.src = dataURL;
video.addEventListener('error', function(err){
// Nothing to see here...
console.log(err);
// Will throw a MediaError code 4
console.log(video.error);
});
}else{
alert("error");
}
});
video.error(function(data,status){
alert("error");
});
This is fine for small videos, but if the video got bigger this is not a good apporach. because in this code video need to be fully loaded in order to do a playback, please suggest me if there is a better apporach
In order not to loose streaming ability, the best (if not the only) way is to secure static videos with an access token passed as an url parameter. I mean you need to give up authentication via token in header.
So you need to make changes to your service on your server to authenticate request by query parameter.
For example your url will look like: www.yourawesomeserver/your_video?access_token=ue873wijweu383j3
Your server should serve the static file if the token is OK.

Resources