Render remote PNG image in AngularJS - angularjs

I am developing an AngularJS application which displays a PNG image retrieved from a server.
If I put the URL (see below) in the browser I can see the image just fine. However, if I want to retrieve such image from my Angular application I cannot manage to display it (although I do receive the data!).
The JS code is the following:
$scope.receivedImage = null;
var url = 'https://subdomain.mydomain.uk/img?latitude=55.57&longitude=-5.16&extent=2000';
$http(
{
method: 'GET',
url: url,
headers: {
Accept: 'image/png'
}
}
).then(
function successCallback(response) {
var data = response.data;
$scope.receivedImage = data;
},
function errorCallback(response) {
console.error(response);
}
);
The problem is that I cannot see the image that is retrieved. To understand better the situation I put in the HTML page the following code:
<div ng-show="receivedImage">
<pre>{{receivedImage}}</pre>
<img data-ng-src="{{receivedImage}}" />
<img data-ng-src="data:image/png;{{receivedImage}}" />
</div>
The '' shows something like
�PNG IHDR�R9�%IDATx��̱ ������ �2��'��j�Z�V��w����LxIEND�B`�
The first '' does not show anything.
The second '' shows an image icon and throws in console an error:
GET
data:image/png;%EF%BF%BDPNG%1A%00%00%00IHDR%00%00%00%1E%00%00%00%1E%08%02%0…%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDL%0E%17x%00%00%00%00IEND%EF%BF%BDB`%EF%BF%BD
net::ERR_INVALID_URL
How can I render this image correctly?

Try setting the ng-src attribute to a variable that is the url.
$scope.url = 'https://subdomain.mydomain.uk/img?latitude=55.57&longitude=-5.16&extent=2000';
and in the markup
<img ng-src="{{url}}" />

If the url is unprotected then the approach from Anthony helps a lot. For my use-case where the URL was protected i had to go with the below approach. In this case i had to inject the authentication headers by overriding angular's http authentication interceptors for accessing the protected URL.
// http call inside a service under a function named getImage()
$http(
{
method: 'GET',
url: 'YOUR_PROTECTED_RESOURCE_URL',
// This is required for getting your data as buffer array
{
responseType: 'arraybuffer'
}
}
).then(
function successCallback(response) {
return response;
},
function errorCallback(response) {
console.error(response);
}
);
Inside your controller or directive the data that comes from the above call has to be handled like so:
// Function to get the image from the server
var handleImage = function(){
MyHttpService.getImage()
.then(function(response){
// Can be used within ng-src fro displaying image
$scope.receivedImage = 'data:image/png;base64,'+_arrayBufferToBase64(response);
}, function(error){
console.error(error);
});
};
// Convert the buffer to base64
var _arrayBufferToBase64 = function( buffer ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
console.log(len);
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
};
Hope this helps someone who is trying to access the resource from a protected resource URI.

Related

ExpressJS IP and AngularJS $http get

I'm trying to learn ExpressJS and I'm having trouble getting IP address from an Express route to display in the browser via Angular controller.
I'm using 2 Nodejs modules (request-ip and geoip2) to get the IP and then lookup geolocation data for that IP. Then trying to use Angular to display the geolocation data in the browser using an Angular $http get call.
My Express route for the IP:
// get IP address
router.get('/ip', function (req, res, next) {
console.log('requestIP is ' + ip);
// geolocation
geoip2.lookupSimple(ip, function(error, result) {
if (error) {
//return res.status(400).json({error: 'Something happened'});//default
return res.sendStatus(400).json({error: 'Something happened'});
}
else if (result) {
return res.send(result);
}
});
});
And my AngularJS controller code:
function MainController($http) {
var vm = this;
vm.message = 'Hello World';
vm.location = '';
vm.getLocation = function() {
$http({
method: 'GET',
url: 'localhost:8000/ip'
}).then(function (result) {
console.log(result);
return vm.location = result;
});
};
};
The Hello World message displays but not the location...? I can also go to localhost:8000/ip and see the JSON result. The result doesn't appear in Chrome's console either. The result is a json object like this:
{"country":"US","continent":"NA","postal":"98296","city":"Snohomish","location":{"accuracy_radius":20,"latitude":47.8519,"longitude":-122.0921,"metro_code":819,"time_zone":"America/Los_Angeles"},"subdivision":"WA"}
I'm not sure why the Hello Word displays and the location doesn't when it seems that I have everything configured correctly... so obviously I'm doing something wrong that I don't see...?
You have initialised 'vm.location' as a string when in fact it is a JSON object.
vm.location = {};
You need to adjust the url paramater in your request to:
url: '/ip'
As you are sending back JSON from Express.js, you should change your response line to:
return res.json(result);
Do you call vm.getLocation() somewhere in your code after this?
The data you need is under result.data from the response object.
Also in order to display the data in the html you have to specify which property to display from the vm.location object (vm.location.country, vm.location.city etc..).
From angular docs about $http:
The response object has these properties:
data – {string|Object} – The response body transformed with the transform functions.
status – {number} – HTTP status code of the response.
headers – {function([headerName])} – Header getter function.
config – {Object} – The configuration object that was used to generate the request.
statusText – {string} – HTTP status text of the response.
Is this express js and angular hosted on the same port? If so please replace your
$http({
method: 'GET',
url: 'localhost:8000/ip'
}).then(function (result) {
console.log(result);
return vm.location = result;
});
with
$http({
method: 'GET',
url: '/ip'
}).then(function (result) {
console.log(result);
return vm.location = result;
});
It may be considered as CORS call and you have it probably disabled.
You can also specify second function to then (look code below) and see if error callback is called.
$http({
method: 'GET',
url: '/ip'
}).then(function (result) {
console.log(result);
return vm.location = result;
}, function (error) {
console.log(error);
});

How to return image from $http.get in AngularJS

In my controller I call a service that returns a promise
var onComplete = function(data) {
$scope.myImage = data;
};
In my service I make a call to get the image by passing url directly to the image itself:
return $http.get("http://someurl.com/someimagepath")
.then(function(response){
return response.data;
});
All the calls are succeeding and the response.data appears to be holding in an image inside:
����JFIF��;CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 90
��C

��C
����"��
���}!1AQa"q2���#B��R��$
although I'm not sure if it actually does because I'm having trouble displaying it. I've tried (inside index.html)
<img ng-src="{{myImage}}">
and
<img ng-src="{{myImage}}.jpeg">
and
<img ng-src="data:image/JPEG;base64,{{myImage}}">
Ideas?
Is it possible to return an actual image from $http.get and convert its response back to image (jpeg, etc.)
Thanks!
None of the methods seems to be complete, this is a complete solution:
$http({
method: 'GET',
url: imageUrl,
responseType: 'arraybuffer'
}).then(function(response) {
console.log(response);
var str = _arrayBufferToBase64(response.data);
console.log(str);
// str is base64 encoded.
}, function(response) {
console.error('error in getting static img.');
});
function _arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
Then I am able to use it directly:
<img data-ng-src="data:image/png;base64,{{img}}">
The function to convert arraybuffer into base64 is directly taken from ArrayBuffer to base64 encoded string
Just in case anyone needs it.
In my case, I had to send the request through angular's $http service, because of various transformers and other fancy stuff we do with it.
So based on the Mozilla's guide mentioned earlier, I came up with the following solution:
let getImageDataURL = (url, imageType = 'image/jpeg') => {
return $http.get(url, {responseType: 'arraybuffer'}).then((res) => {
let blob = new Blob([res.data], {type: imageType});
return (window.URL || window.webkitURL).createObjectURL(blob);
});
};
The basic idea is to set the responseType property of the underlying XHR request and the convert the binary content to data URL.
The image that's coming back is in binary encoding, rather than Base64.
Understandably, <img> tags don't support sourcing from binary through attributes, so you'll have to look at another solution.
You could try converting the binary encoding to Base64 at the client side using TypedArrays together with the btoa function. Then you'd be able to use
<img ng-src="data:image/JPEG;base64,{{myImage}}">
This guide a by Mozilla covers making an XHR request for and image and reading it directly into a UInt8Array. It should be a good starting place.
It's written for plain old Javascript, but translating it to Angular should be a good exercise if you are just learning the ropes.
By way of https://stackoverflow.com/a/43032560/418819, you can use "blob" as the responseType and very neatly get the data url with a FileReader.
$http.get( url, { responseType: "blob" } ).then((result) => {
var reader = new FileReader();
reader.readAsDataURL( result.data );
reader.onload = function (e) {
return e.target.result;
};
});
You can reference it like so:
<img data-ng-src="{{img}}">

Upload image to server angularjs

I need to upload an image taken from my mobile device to my server. I found the angular-upload library to which makes reference. I need to do is to transform the image base 64, send it by post to my server because the server is where I will work with her. And the other, send from my server and work it from the application to run.
var server = URL_BASE+'addView/';
var trustAllHosts = true;
var ftOptions = new FileUploadOptions();
ftOptions.fileKey = 'file';
ftOptions.fileName = $scope.imagen.substr($scope.imagen.lastIndexOf('/') + 1);
ftOptions.mimeType = 'image/jpeg';
ftOptions.httpMethod = 'POST';
console.log(ftOptions);
$cordovaFileTransfer.upload(encodeURI(server), $scope.imagen, ftOptions, trustAllHosts)
.then(function(result) {
console.log(result)
}, function(err) {
// Error
console.log(err);
}, function (progress) {
});
ionic file transfer
I'm personally using Cordova file transfer for upload & download content from a server.
Base64 encoding
Don't know where is your image stored and how you retrieve it, but, either you specify that the image is base64 encode into the HTML file delimiter
OR
You transform your image using a canvas
See that post for more info : https://stackoverflow.com/a/20285053/3687474
You haven't specified what you really need so:
Here you have a factory
//Factory you register on your module
angular
.module('myApp')
.factory('sendBase64Image', sendBase64Image)
function sendBase64Image($http) {
var urlBase; //url to be filled in
var base64 = {};
base64.sendBase = function (baseImg) {
var request = $http({
method: "post",
url: urlBase,
headers: {
'Content-type': 'application/json'
},
data : baseImg
});
}
return base64;
}
You should then inject it via dependency injection to your controller and perform call to the server.
If you want to do something with a response use success() method to handle promise response.

AngularJS $http ajax request is not asynchronous and causes page to hang

I have a service where I am pulling data from server. When I click the button to send out the request to server through this service, the window freezes until I receive a response from server. Is there anything I can do to make this request asynchronous ?
Here is my service.
app.factory('service', function($http) {
return {
getLogData : function(startTime,endTime){
return $http({
url: baseURL + 'getLogData',
method: 'GET',
async: true,
cache: false,
headers: {'Accept': 'application/json', 'Pragma': 'no-cache'},
params: {'startTime': startTime , 'endTime': endTime}
});
}
};
)};
HTML.
<button ng-click="getData()">Refresh</button>
<img src="pending.gif" ng-show="dataPending" />
Code
$scope.getData = function(){
service.getLogData().success(function(data){
//process data
}).error(function(e){
//show error message
});
}
While there is some argument about the pros and cons of your approach, I am thinking that the problem is answered here: AJAX call freezes browser for a bit while it gets response and executes success
To test if this in fact part of the problem, dummy up a response and serve it statically. I use Fiddler or WireShark to get the response and then save to a file like testService.json. XHR and all of it's various derivatives like $HTTP $.ajax see it as a service though the headers might be slightly different.
Use the success promise, and wrap up the log data in a set of objects that you can attach to a $scope.
So instead of having your service have a blocking method, have it maintain a list of "LogEntries".
// constructor function
var LogEntry = function() {
/*...*/
}
var logEntries = [];
// Non-blocking fetch log data
var getLogData = function() {
return $http({
url : baseURL + 'getLogData',
method : 'GET',
async : true,
cache : false,
headers : { 'Accept' : 'application/json' , 'Pragma':'no-cache'},
params : {'startTime' : startTime , 'endTime' : endTime}
}).success(function(data) {;
// for each log entry in data, populate logEntries
// push(new LogEntry( stuff from data ))...
};
}
Then in your controller, inject your service and reference this service's log data array so Angular will watch it and change the view correctly
$scope.logEntries = mySvc.logEntries;
Then in the HTML, simply do something over logEntries:
<p ng-repeat="logEntry in logEntries">
{{logEntry}}
</p>
use this code to config
$httpProvider.useApplyAsync(true);
var url = //Your URL;
var config = {
async:true
};
var promise= $http.get(url, config);
promise.then(
function (result)
{
return result.data;
},
function (error)
{
return error;
}
);

change Content-type to "application/json" POST method, RESTful API

I am new at AngularJS and I needed your help.
All I need just need is to POST my json to the API and recieve the proper response.
Here's my JSON where i don't know where to code this.
JSON
{
"userId" :"testAgent2",
"token" :"testAgent2",
"terminalInfo":"test2",
"forceLogin" :"false"
}
NOT SURE IF I'm doing this right.
CONTROLLER.JS
function UserLoginCtrl($scope, UserLoginResource) {
//Save a new userLogin
$scope.loginUser = function() {
var loggedin = false;
var uUsername = $scope.userUsername;
var uPassword = $scope.userPassword;
var uforcelogin = 'true';
UserLoginResource.save();
}
}
SERVICES.JS
angular.module('UserLoginModule', ['ngResource'])
.factory('UserLoginResource', function($resource, $http) {
$http.defaults.useXDomain = true;
delete $http.defaults.headers.common['X-Requested-With'];
$http.defaults.headers.post["Content-Type"] = "application/json"; //NOT WORKING
return $resource('http://123.123.123.123\\:1234/SOME/LOCATION/THERE', {}, {
save: {
method:'POST',
headers: [{'Content-Type': 'application/json'}]
} //NOT WORKING EITHER
});
});
INDEX.HTML
<html ng-app>
<head>
<script src="js/lib/angular/angular.js"></script>
<script src="js/lib/angular/angular-resource.js"></script>
</head>
<body ng-controller="UserLoginCtrl">
<form class="form-horizontal" name="form-horizontal" ng-submit="loginUser();">
<div class="button-login">
<!-- start: button-login -->
<button class="btn btn-primary" type="submit">Login</button>
</div>
</form>
</body>
</html>
I kept on getting a response like Unsupported Media Type. I don't know, what else to do.
Assuming you are able to use one of the more recent "unstable" releases, the correct syntax to change the header is.
app.factory('BarService', function ($resource) {
var BarService = $resource('/foo/api/bars/:id', {}, {
'delete': {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
}
}
});
return BarService;
});
I find the $resource service is a tremendously powerful tool for building applications and has matured to a point that you do not need to fall back to $http as much. Plus its active record like patterns are damn convenient.
Posting a JSON object is quite easy in Angular. All you need to do is the following:
Create a Javascript Object
I'll use your exact properties from your code.
var postObject = new Object();
postObject.userId = "testAgent2";
postObject.token = "testAgent2";
postObject.terminalInfo = "test2";
postObject.forceLogin = "false";
Post the object to the API
To post an object to an API you merely need a simple $http.post function. See below:
$http.post("/path/to/api/", postObject).success(function(data){
//Callback function here.
//"data" is the response from the server.
});
Since JSON is the default method of posting to an API, there's no need to reset that. See this link on $http shortcuts for more information.
With regards to your code specifically, try changing your save method to include this simple post method.
The right way to set 'Content-Type': 'application/json' is setting a transformRequest function for the save action.
angular.module('NoteWrangler')
.factory('NoteNgResource', function NoteNgResourceFactory($resource) {
// https://docs.angularjs.org/api/ngResource/service/$resource
return $resource("./php/notes/:id", {}, {
save : { // redefine save action defaults
method : 'POST',
url : "./php/notes", // I dont want the id in the url
transformRequest: function(data, headers){
console.log(headers);
headers = angular.extend({}, headers, {'Content-Type': 'application/json'});
console.log(headers);
console.log(data);
console.log(angular.toJson(data));
return angular.toJson(data); // this will go in the body request
}
}
});
});
It seems there isn't a method to clear query parameters, the request will have both...

Resources