Search code examples
javascriptangularjsmongodbangularjs-directivebinaryfiles

Extract a binary file content in byte array using angularjs


I would like to extract binary data from a binary file into a byte array. I am having difficulty getting it to work correctly.

You can see the jsFiddle here: https://jsfiddle.net/alexsuch/6aG4x/

The HTML:

<div ng-controller="MainCtrl" class="container">
  <h1>Select text file</h1>
    <input type="file" on-read-file="showContent($fileContent)" />
    <div ng-if="content">
        <h2>File content is:</h2>
        <pre>{{ content }}</pre>
    </div>
</div>

The Javascript code:

var myapp = angular.module('myapp', []);

myapp.controller('MainCtrl', function ($scope) {
    $scope.showContent = function($fileContent) {
        $scope.content = $fileContent;
    };
});

myapp.directive('onReadFile', function ($parse) {
    return {
        restrict: 'A',
        scope: false,
        link: function(scope, element, attrs) {
            var fn = $parse(attrs.onReadFile);

            element.on('change', function(onChangeEvent) {
                var reader = new FileReader();

                reader.onload = function(onLoadEvent) {
                    scope.$apply(function() {
                        fn(scope, {$fileContent:onLoadEvent.target.result});
                    });
                };

                reader.readAsText((onChangeEvent.srcElement || onChangeEvent.target).files[0]);
            });
        }
    };
});

I get a corrupted text format as shown in this: enter image description here

What am I doing wrong that's causing the content to be garbled like this?


Solution

  • You say you want the file's binary content to send over JSON and store in mongoDB. The problem with your code is that you're reading the file as text, when you should be reading it as an ArrayBuffer, which won't apply text encoding to the binary values.

    ArrayBuffer is great, but not all browsers will support sending ArrayBuffer over JSON via XMLHttpRequest. Especially if you know the format it needs to be in, it might be a good idea to convert it to a regular array. Luckily, we can use typed arrays in JavaScript to help that.

    var myapp = angular.module('myapp', []);
    
    myapp.controller('MainCtrl', function ($scope) {
        $scope.showContent = function($fileContent) {
            $scope.content = $fileContent;
        };
    });
    
    myapp.directive('onReadFile', function ($parse) {
        return {
            restrict: 'A',
            scope: false,
            link: function(scope, element, attrs) {
                var fn = $parse(attrs.onReadFile);
    
                element.on('change', function(onChangeEvent) {
                    var reader = new FileReader();
    
                    reader.onload = function(onLoadEvent) {
                        var buffer = onLoadEvent.target.result;
                        var uint8 = new Uint8Array(buffer); // Assuming the binary format should be read in unsigned 8-byte chunks
                        // If you're on ES6 or polyfilling
                        // var result = Array.from(uint8);
                        // Otherwise, good old loop
                        var result = [];
                        for (var i = 0; i < uint8.length; i++) {
                          result.push(uint8[i]);
                        }
    
                        // Result is an array of numbers, each number representing one byte (from 0-255)
                        // On your backend, you can construct a buffer from an array of integers with the same uint8 format
                        scope.$apply(function() {
                            fn(scope, {
                              $fileContent: result
                            });
                        });
                    };
    
                    reader.readAsArrayBuffer((onChangeEvent.srcElement || onChangeEvent.target).files[0]);
                });
            }
        };
    });
    

    Updated fiddle: https://jsfiddle.net/6aG4x/796/