14

I've created an AngularJS application that loads images from an OAuth secured backend. My OAuth library is configured by adding an extra interceptor to the Angular $httpProvider to add the correct authentication header.

My source code in my HTML template looks like this:

<img ng-src="{{ '/users/1/images/' + imageId }}">

My problem is that the http request that is created by the ngSrc directive is ignored by the $http implementation, which results in a request with the wrong authentication. The normal API calls function correct (the ones invoked by my controllers/services by using $http and $resource directly).

Mats Stijlaart
  • 5,058
  • 7
  • 42
  • 58

4 Answers4

23

Based on Michal's answer the directive could look like the below

app.directive('httpSrc', [
        '$http', function ($http) {
            var directive = {
                link: link,
                restrict: 'A'
            };
            return directive;

            function link(scope, element, attrs) {
                var requestConfig = {
                    method: 'Get',
                    url: attrs.httpSrc,
                    responseType: 'arraybuffer',
                    cache: 'true'
                };

                $http(requestConfig)
                    .then(function(response) {
                        var arr = new Uint8Array(response.data);

                        var raw = '';
                        var i, j, subArray, chunk = 5000;
                        for (i = 0, j = arr.length; i < j; i += chunk) {
                            subArray = arr.subarray(i, i + chunk);
                            raw += String.fromCharCode.apply(null, subArray);
                        }

                        var b64 = btoa(raw);

                        attrs.$set('src', "data:image/jpeg;base64," + b64);
                    });
            }

        }
    ]);

You would then use it as follows

<img http-src="www.test.com/something.jpeg">
Nick Grealy
  • 24,216
  • 9
  • 104
  • 119
Bradley MIller
  • 564
  • 1
  • 4
  • 7
  • Hi there, I am using this directive - the problem seems to be that http-src as defined is not being recomputed at each iteration, so if I point it to an image that keeps changing, it does not update. Any thoughts? – user1361529 Jan 23 '16 at 01:15
  • 1
    @user1361529 Watch the attribute for changes and then re execute the request. http://jsfiddle.net/83aaecyy/ – Bradley MIller Feb 02 '16 at 12:37
  • works with dynamic url too: `` – Aakash Feb 20 '16 at 11:24
  • u save my life today – Jet Geng May 19 '16 at 02:41
  • What's the point of iterating the response data in smaller chunks? I understand that if the buffer would not yet be read fully then it would be beneficial to gradually read from the pipe to not fill the memory but in this case I think the string is already fully in memory and splitting and going it though in slices only takes more memory. – mikkom Sep 04 '18 at 05:30
  • Really this is awesome directive. You save my night and day as well, Keep it up bro – Rajesh Pal Jul 25 '20 at 19:55
11

The ngSrc directive just controls the usual src attribute for an img, which makes the browser request the image just like any other image, not via Javascript, so I don't think this can be intercepted.

However, what you could do, is write a new directive, save httpSrc. This then can fetch the image using a call to $http, and then create a data URI with the response using something like Using raw image data from ajax request for data URI (although I'm not sure of the browser support for it).

Community
  • 1
  • 1
Michal Charemza
  • 25,940
  • 14
  • 98
  • 165
  • Thanks. I was already considering this, but I worried about the performance impact on the DOM when I add multiple chunks of Base64 to it. I hoped there would be a clean and elegant solution. – Mats Stijlaart Jan 08 '14 at 15:00
5

As said here you can use angular-img-http-src (bower install --save angular-img-http-src if you use Bower).

If you look at the code, it uses URL.createObjectURL and URL.revokeObjectURL which are still draft on 19 April 2016. So look if your browser supports it.

In order to use it, declare 'angular.img' as a dependency to your app module (angular.module('myapp', [..., 'angular.img'])), and then in your HTML you can use http-src attribute for <img> tag.

In your example, you can write: <img http-src="/users/1/images/{{imageId}}">

Community
  • 1
  • 1
Anthony O.
  • 22,041
  • 18
  • 107
  • 163
0

This is a little old, but thought I would add another method for finding an image w/o using an interceptor. In my case, I needed to find the image path from the rails assets pipeline.

There was a blog from Codetunes (http://codetunes.com/2014/5-tips-on-how-to-use-angularjs-with-rails-that-changed-how-we-work/) that described how they find templates. This makes use of an interceptor but doesn't work for images.

The solution I used requires you to use a model for the ng-src value. Within the model, I call a helper method from a service that utilizes the Rails.templates variable setup from the above blog article.

So, it looks like the following:

angular.module('myApp').factory('RailsAssetService', function(Rails) {

    function RailsAssetService() {

    }

    RailsAssetService.getAssetsUrl = function(url) {
        var railsUrl = Rails.templates[url];
        if (railsUrl === undefined)
            railsUrl = url;
        return railsUrl;
    }

    return RailsAssetService;
});

and in the controller:

vm.creditCardsImage = RailsAssetService.getAssetsUrl('credit-cards-logo.gif');

and in the view:

<img ng-src="{{vm.creditCardsImage}}">
Ken Joyner
  • 921
  • 9
  • 14