AngularJS Upload a file and send it to a DB

nikk wong picture nikk wong · Jun 13, 2015 · Viewed 8.6k times · Source

I've been trying to get ngFileUpload working so that I can upload images and have them sent to a DB–in my case a mongoLab DB which accepts JSON objects that can be POSTed with a syntax like this:

$http.post('myMongoName/myDb/myCollection/myTable', {'key': 'value'})

Fairly simple. However, I'm confused on how to send images uploaded using ngFileUpload to the DB. I'm using the familiar syntax introduced on ngFileUpload's documentation page:

$scope.upload = function (files) {
        if (files && files.length) {
            for (var i = 0; i < files.length; i++) {
                var file = files[i];
                console.log(file);
             Upload.upload({
                url: myMongoLabURL,
                fields: {'sup': 'sup'},
                file: file
                })
             }).success(function (data, status, headers, config) {
                    console.log('file ' + config.file.name + 'uploaded. Response: ' + data);
                });
            }
        }
 }

But, upon logging the file object out, I get the object:

File {}
$$hashKey: "object:76"
lastModified: 1433922719000
lastModifiedDate: Wed Jun 10 2015 00:51:59 GMT-0700 (PDT)
name: "1.png"
size: 138024
type: "image/png"
webkitRelativePath: ""
__proto__: File 

None of which, contain the actual image binary which could be stored to the DB. I essentially have no idea where the actual image itself is actually being uploaded to!

It's also important to note that I am not getting a response from the server with this syntax—though, if I could get the image's binary I could just use the familiar $http.post method and push the image into the DB myself.

How can I find the uploaded image's binary, and push it into the DB? Where does the image exist, after being uploaded—and where's it even being uploaded to? I'm doing this all on localhost so it seems that the browser has read all the properties of the image, but I'm not sure how to turn this into meaningful insight that I can use to store the image into my external DB.

Thanks for any help.

Answer

nikk wong picture nikk wong · Aug 18, 2015

I ended up using the FileReader API. Most specifically, reading the URI as a blob. I implemented something like the following in a custom directive. I ended up including a lot of attrs reading which would allow the directive to be used multiple times in isolated scopes on the same page—but I'll leave that out here. I can provide you with the entire directive if it'd be helpful though—but briefly, it looked something like this:

function create_blob(file) {
    var deferred = $q.defer();
    var reader = new FileReader();
    reader.onload = function() {
            deferred.resolve(reader.result);
    };
    reader.readAsDataURL(file);
    return deferred.promise;
}
$scope.$watch('files', function() {
    $scope.upload($scope.files);
});
$scope.upload = function(files) {
    $scope.loading = true;
    if (files && files.length) {
        for (var i = 0; i < files.length; i++) {
            var file = files[i];
            var promise = create_blob(file);
            promise.then(function(result) {
                var thumb = resize(result);
                Upload.http({
                    url: '/path',
                    data: result
                })
                .progress(function (evt) {
                    console.log(evt);
                    var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
                    console.log('progress: ' + progressPercentage + '% ');
               })
                .success(function (data, status,headers,config) {

...etc.

I think that will do it for you. Getting the blob data can be confusing because when you upload the image you can't see any of it's data in the scope—though the rest of the image's meta data will show up! This create_blob function should get it for you though in blob format. Try doing a console.log(reader.readAsDataURL(file); inside that function to see the data directly.

It took me a long time to figure out, and I'm not sure if this is the best way to do it—if anyone knows any better, please feel free to suggest! :-)