Search code examples
javascriptarraybufferbinarystream

Migrate FileReader ReadAsBinaryString() to ReadAsArrayBuffer() or ReadAsText()


I realize that the new Mozilla Firefox return allocation size overflow (on FileReader.ReadAsBinaryString()) when the file bigger than 200MB (something like that).

Here's some of my code on test for client web browser:

function upload(fileInputId, fileIndex)
{
    var file = document.getElementById(fileInputId).files[fileIndex];
    var blob;
    var reader = new FileReader();
    reader.readAsBinaryString(file); 
    reader.onloadend  = function(evt)
    {
        xhr = new XMLHttpRequest();

        xhr.open("POST", "upload.php", true);

        XMLHttpRequest.prototype.mySendAsBinary = function(text){
            var data = new ArrayBuffer(text.length);
            var ui8a = new Uint8Array(data, 0);
            for (var i = 0; i < text.length; i++){ 
                ui8a[i] = (text.charCodeAt(i) & 0xff);
            }

            if(typeof window.Blob == "function")
            {
                 blob = new Blob([data]);
            }else{
                 var bb = new (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder)();
                 bb.append(data);
                 blob = bb.getBlob();
            }

            this.send(blob);
        }

        var eventSource = xhr.upload || xhr;
        eventSource.addEventListener("progress", function(e) {
            var position = e.position || e.loaded;
            var total = e.totalSize || e.total;
            var percentage = Math.round((position/total)*100);
        });

        xhr.onreadystatechange = function()
        {
            if(xhr.readyState == 4)
            {
                if(xhr.status == 200)
                {
                    console.log("Done");
                }else{
                    console.log("Fail");
                }
            }
        };
        xhr.mySendAsBinary(evt.target.result);
    };
}

So I tried change it to FileReader.ReadAsArrayBuffer(), the error has not shown up but the data are not the same (as it's not read as binary string).

Did anyone has any solution to solve this problem? Is there any way that we can upload bigger file from JS to Web Server in raw/string other than FileReader implementation?

I read on Mozilla JS Documentation that said:

This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future. - Mozilla

If not ReadAsBinaryString, the how to implement ReadAsArrayBuffer or ReadAsText


Solution

  • Kaiido statement is correct

    To send Files to a web-server, you simply don't need js

    But that doesn't answer my question. Using the Simple XMLHttpRequest() can upload the file and track those progress as well. But still, it's not it. The direct upload, either from the <form> or using XMLHttpRequest() will need to increase your upload limit in php setting. This method is not convenience for me. How if the client upload file as 4GB? So I need to increase to 4GB. Then next time, client upload file as 6GB, then I have to increase to 6GB.

    Using the slice() method is make sense for bigger file as we can send it part by part to server. But this time I am not using it yet.

    Here's some of my test the worked as I want. I hope some expert could correct me if I am wrong. My Upload.js

    function upload(fileInputId, fileIndex)
    {
        var file = document.getElementById(fileInputId).files[fileIndex];
        var blob;
        var reader = new FileReader();
    
        reader.readAsArrayBuffer(file);
        reader.onloadend  = function(evt)
        {
            xhr = new XMLHttpRequest();
            xhr.open("POST", "upload.php?name=" + base64_encode(file.name), true);
    
            XMLHttpRequest.prototype.mySendAsBinary = function(text){
                var ui8a = new Uint8Array(new Int8Array(text));
    
                if(typeof window.Blob == "function")
                {
                     blob = new Blob([ui8a]);
                }else{
    
                    var bb = new (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder)();
                    bb.append(ui8a);
                    blob = bb.getBlob();
                }
    
                this.send(blob);
            }
    
            var eventSource = xhr.upload || xhr;
            eventSource.addEventListener("progress", function(e) {
                var position = e.position || e.loaded;
                var total = e.totalSize || e.total;
                var percentage = Math.round((position/total)*100);
                console.log(percentage);
            });
    
            xhr.onreadystatechange = function()
            {
                if(xhr.readyState == 4)
                {
                    if(xhr.status == 200)
                    {
                        console.log("Done");
                    }else{
                        console.log("Fail");
                    }
                }
            };
    
            xhr.mySendAsBinary(evt.target.result);
        };
    }
    

    Below is how the PHP server listen to the ArrayBuffer from JS

    if(isset($_GET["name"])){
        $name = base64_decode($_GET["name"]);
        $loc = $name;
        $inputHandler = fopen('php://input', "r");
        $fileHandler = fopen($loc, "w+");
    
        while(true) {
            //$buffer = fgets($inputHandler, 1024);
            $buffer = fread($inputHandler, 1000000);
    
            if (strlen($buffer) == 0) {
                fclose($inputHandler);
                fclose($fileHandler);
                return true;
            }
    
            //$b = base64_encode($buffer);
    
            fwrite($fileHandler, $buffer);
        }
    }
    

    The above method works well. The FileReader read the file as ArrayBuffer the upload to server. For me, migrating from ReadAsBinaryString() to ReadAsArrayBuffer() is important and ReadAsArrayBuffer() has some better performance rather than ReadAsBinaryString()

    Here's some reason, why some developer relies to FileReader API:

    1. Streaming. Using this method, the file will be stream, so we can avoid setting the php multiple time.
    2. Easy Encrypt. As the file is sending via ArrayBuffer, it is easy for developer to Encrypt the file while upload in progress.

    This method also support upload any type of file. I ve done some test and I realize that ReadAsArrayBuffer() method are more faster than ReadAsBinaryString() and direct form upload. You may try it.

    Security Notice

    The above code is only under test code, to use it in production, you have to consider sending the data in GET or POST under HTTPS.