将图像作为服务加载

时间:2017-11-25 15:41:09

标签: angularjs

我有从API中提取对象的服务。某些对象可能包含图像URL。后端当前扫描这些,并通过get_file_contents()处理它们(在PHP中)并将它们转换为内联数据。这大大加载了我服务器上的吞吐量。我这样做的原因是因为我想稍后缓存图像以便脱机,但是我仍然可以使用常规角度来渲染对象。

我无法在$http.get()的浏览器中使用Javascript进行处理,因为托管图像的网站阻止了跨网站请求。我想要做的是在浏览器中创建一个<IMG>元素,一旦加载就调用该服务,这样我就可以提取数据并用它处理对象。

我无法控制服务工作者从应用程序内部存储get,并且应用程序在下载API对象之前随时都不知道URL。

我确实考虑过重做服务工作者来存储来自我网站的内容,但这似乎有点不对,而且我不确定它的效果如何,加上,在开发时,我关掉了服务工作者,因为这意味着我必须让整个网站加载两次才能完全刷新。

有人可以帮助我通过浏览器将图像数据导入我的服务吗?

1 个答案:

答案 0 :(得分:-1)

如果我首先找到了CORS支持图像主机,我可能根本不需要它,可能刚刚使用$http电话。

需要指令,服务和控制器,以及支持CORS的主机(例如Imgur)。我还使用了this base64 canvas code

以下是javascript代码:

// Using this from https://stackoverflow.com/questions/934012/get-image-data-in-javascript
function getBase64Image(img) {
    // Create an empty canvas element
    var canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;

    // Copy the image contents to the canvas
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);

    // Get the data-URL formatted image
    // Firefox supports PNG and JPEG. You could check img.src to
    // guess the original format, but be aware the using "image/jpg"
    // will re-encode the image.
    var dataURL = canvas.toDataURL("image/png");

    return dataURL;
    //  return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}

// Used on the img tag to handle the DOM notification feeding into the service
app.directive('notifyimgsvc', function() {
    return {restrict : 'A', link : function(scope, element, attrs) {
        element.bind('load', function() {
            console.log('imgSvc::notify() image is loaded');
            console.log("imgSvc::notify(): " + this.src);
            imgSvc.notifyLoad(this.src, getBase64Image(this));

        });
        element.bind('error', function() {
            console.log('imgSvc::notify() image could not be loaded');
            console.log("imgSvc::notify(): " + this.src);
        });
    }};
});

// A core service to handle the comms in both directions from requests to data
app.service('imgSvc', [function(netSvc) {
    imgSvc = this; // to avoid ambiguoity in some inner function calls

    imgSvc.images = {}; // a cache of images
    imgSvc.requests = []; // the requests and their callbacks
    imgSvc.handlers = []; // handlers that will render images

    console.log("imgSvc::init()");

    // Allows a controller to be notified of a request for an image and
    // a callback to call when an image is added. There should only ever
    // be one of these so an array is probaby not needed and any further
    // requests should probably throw an error.
    imgSvc.registerHandler = function(callback) {
        console.log("imgSvc::registerHandler()");
        if (imgSvc.requests.length) {
            // Already have image requests, so tell the new handler about them
            for ( var i in imgSvc.requests) {
                callback(imgSvc.requests[i].url);
            }
        }
        // Add the new handler to the stack
        imgSvc.handlers.push(callback);
    };

    // The usage function from your code, provide a callback to get notified
    // of the data when it loads.
    imgSvc.getImg = function(url, callback) {
        console.log("imgSvc::getImg('" + url + "')");

        // If we have pre-cached it, send it back immediately.
        if (imgSvc.images[url] != undefined) {
            console.log("imgSvc::getImg('" + url + "'): Already have data for this one");
            callback(url, imgSvc.images[url]);
            return;
        }

        // push an object into the request queue so we can process returned data later.
        // Doing it this way als means you can have multiple requests before any data
        // is returned and they all get notified easily just by looping through the array.
        var obj = {"url" : url, "callback" : callback};
        if (imgSvc.handlers.length) {
            console.log("imgSvc::getImg('" + url + "'): informing handler");
            for ( var i in imgSvc.handlers) {
                imgSvc.handlers[i](obj.url);
            }
        }
        imgSvc.requests.push(obj);
    };

    // Notification of a successful load (or fail if src == null).
    imgSvc.notifyLoad = function(url, src) {
        console.log("imgSvc.notifyLoad()");
        // Save the data to the cache so any further calls can be handled
        // immediately without a request being created.
        imgSvc.images[url] = src;

        // Go though the requests list and call any callbacks that are registered.
        if (imgSvc.requests.length) {
            console.log("imgSvc.notifyLoadCallback('" + url + "'): scanning requests");
            for (var i = 0; i < imgSvc.requests.length; i++) {
                if (imgSvc.requests[i].url == url) {
                    console.log("imgSvc.notifyLoadCallback('" + url + "'): found request");
                    // found the request so remove it from the request list and call it
                    var req = imgSvc.requests.splice(i, 1)[0];
                    i = i - 1;
                    console.log("imgSvc.notifyLoadCallback('" + url + "')");
                    req.callback(url, src);
                } else {
                    console.log("imgSvc.notifyLoadCallback('" + url + "'): skipping request for '" + imgSvc.requests[i].url + "'");
                }
            }
        } else {
            console.log("imgSvc.notifyLoadCallback('" + url + "'): No requests present??");
        }
    };

    // The notifiy fail is just a logging wrapper around the failure.
    imgSvc.notifyFail = function(url) {
        console.log("imgSvc.notifyFail()");
        imgSvc.notifyLoad(url, null);
    };
}]);

// A simple controller to handle the browser loading of images.
// Could probably generate the HTML, but just doing simply here.
app.controller('ImageSvcCtrl', ["$scope", function($scope) {
    $scope.images = [];
    console.log("imgSvcCtrl::init()");

    // Register this handler so as images are pushed to the service,
    // this controller can render them using regular angular.
    imgSvc.registerHandler(function(url) {
        console.log("imgSvcCtrl::addUrlHandler('" + url + "')");
        // Only add it if we don't hqve it already. The caching in the
        // service will handle multiple request for the same URL, and
        // all associated malarkey
        if ($scope.images.indexOf(url) == -1) {
            $scope.images.push(url);
        }
    });

}]);

您需要的HTML非常简单:

<div data-ng-controller="ImageSvcCtrl" style="display:none;">
    <img data-ng-repeat="img in images" data-ng-src="{{img}}" alt="loading image" crossorigin="anonymous" notifyimgsvc />
</div>

你可以在控制器内调用它:

var req_url = "https://i.imgur.com/lsRhmIp.jpg";
imgSvc.getImg(req_url, function(url, data) {
    if(data) {
        logger("MyCtrl.notify('" + url + "')");
    } else {
        logger("MyCtrl.notifyFailed('" + url + "')");
    }
});