2

I have a file input. On the change event I read the specified file to post the data within a JSON object to an API.

function changeHandler( event ) {
    const fileReader = new FileReader();
    fileReader.addEventListener(
        'loadend',
        function ( event )
        {
            console.log( CryptoJS.MD5( event.originalTarget.result ).toString() );
            /** ... */
        }
    );
    fileReader.readAsBinaryString( event.originalTarget.files[ 0 ] );
}

In my testing I tried to read the composer.phar. It states out the MD5 checksum isn't the real checksum. So it's clear the FileReader doesn't read the file correctly. Any assumptions what happpens? Maybe the FileReader uses some encoding?

To clarify my needs

While I send the data to a REST API there's no request with multipart/form-data. Thus I cannot use PHP's $_FILES server side.

I need to send the file within a JSON object with content type application/json within the request body. Thus I need to read the file manually client side and to store it server side. And currently the client side data isn't guaranteed while the MD5 checksum of the read data doesn't match the real MD5 checksum of the binary file selected.

A sample request body

{
    "id": "d0f11715-c0b1-4a1d-b471-4408c78fbe01",
    "data": "This is some JSON encoded string of possibly binary file data."
}

Update

I states out the length of the data read is exactly the same as the original file size.

Update

This question has been flagged a duplicate but the mentioned answere didn't resolve my issue. I tried both examples mentioned.

https://stackoverflow.com/a/34493461/2323764

The MD5 checksum calculated by bash cmd md5sum

56f13c034e5e0c58de35b77cbd0f1b0b  resources/composer.phar
function changeHandler( event ) {
    const fileReader = new FileReader();
    fileReader.addEventListener(
        'loadend',
        function ( event )
        {
            console.log( CryptoJS.MD5( event.target.result ).toString() );
        }
    );
    fileReader.readAsBinaryString( event.target.files[ 0 ] );
}

// outputs 784102b153672149418605c1d4abcc3c
function changeHandler( event ) {
    const fileReader = new FileReader();
    fileReader.addEventListener(
        'loadend',
        function ( event )
        {
            const wordArray = CryptoJS.lib.WordArray.create( event.target.result );
            console.log( CryptoJS.MD5( wordArray ).toString() );
        }
    );
    fileReader.readAsArrayBuffer( event.target.files[ 0 ] );
}

// outputs 231bc133db6bdf3f665b53b0d2f631d1
codekandis
  • 712
  • 1
  • 11
  • 22
  • you should probably use a blob instead of a string. – dandavis Oct 21 '20 at 20:47
  • Only Firefox accepts `event.originalTarget`. I would just use `this.files[0]` personally. I would also, use a `load` Event, so there won't be an issue with that Listener. – StackSlave Oct 21 '20 at 20:51
  • @RandyCasburn, that would return an `ArrayBuffer`, but the argument of `CryptoJS.MD5` takes a String. You would just want to `fileReader.readAsArrayBuffer` anyways if you wanted an `ArrayBuffer`, yet once again OP wants a String. Use `fileReader.readAsText` instead. – StackSlave Oct 21 '20 at 21:00
  • @StackSlave `readAsText()` has another checksum, but yet a false one. – codekandis Oct 21 '20 at 21:12
  • @StackSlave In fact I currently just use `MD5()` for testing the read data to check its integrity. I'd use `readAsArrayBuffer()` if I'd just know how to convert it to a string (with, indeed, the right checksum) to send it as a property in a JSON object. – codekandis Oct 21 '20 at 21:27
  • Use an ArrayBuffer, certainly CryptoJs needs some magic for codepoints outside of 0-255 range. – Kaiido Oct 21 '20 at 22:46
  • `readAsText` then `btoa(this.result)` – StackSlave Oct 21 '20 at 23:08
  • This is useless while the text read still seems corrupt. If the calculated checksum matches teh expected one, THEN I can use Base64 or whatever works. – codekandis Oct 21 '20 at 23:10
  • The text read is not "corrupt" it just breaks each character with a codepoint higher than 255 into two. Your library is broken in that it should never have been designed to take DOMStrings as input. Are you sure you included both scripts the answer there told you to include? – Kaiido Oct 21 '20 at 23:27
  • Its a binary file. I don't expect to have characters longer than 8 bits. This is why I assumed it's some kind of encoding problem. I included all 3 scripts: core.js, lib-typedarrays.js, md5.js At least with the array buffer kind of solution I was hopeful, but nada. – codekandis Oct 21 '20 at 23:30
  • https://stackoverflow.com/questions/34492637/how-to-calculate-md5-checksum-of-blob-using-cryptojs#comment100543524_34493461 This one had the same problem. I was diggin' through all revisions. I tried them all. Nothing worked. – codekandis Oct 21 '20 at 23:33
  • 1
    What about an other lib? https://github.com/satazor/js-spark-md5 and when you convert your binary file as string, that's where the encoding issues arise. Even a DOM string will interpret the binary data as characters, loosing info in the process. – Kaiido Oct 21 '20 at 23:34
  • I'm going to try it. ... I tried using a `DataView` over the read `ArrayBuffer`. I tried `TextDecoder` with ALL mentioned encodings on MDN. – codekandis Oct 21 '20 at 23:38
  • @Kaiido JAWOLLJA! It works. Thank you alot. I just updated my question while it's locked. May some mod can unlock it so I can post my solution as an answere. – codekandis Oct 22 '20 at 00:06
  • In case there is no notification, I did reopen. – Kaiido Oct 22 '20 at 02:43

1 Answers1

0

I changed the used library as mentioned by @kaiido.
https://github.com/satazor/js-spark-md5

function changeHandler( event )
        {
            const fileReader = new FileReader();
            fileReader.addEventListener(
                'loadend',
                function ( event )
                {
                    const arrayBuffer = new SparkMD5.ArrayBuffer();
                    arrayBuffer.append( event.target.result );
                    console.log( arrayBuffer.end() );

                    base64Encoded = btoa(
                        [].reduce.call(
                            new Uint8Array( event.target.result ),
                            function ( p, c )
                            {
                                return p + String.fromCharCode( c )
                            },
                            ''
                        )
                    );
                }
            );
            fileReader.readAsArrayBuffer( event.target.files[ 0 ] );
        }

So while now it's clear the read data is correct I just have to process the data read. The checksum of the Base64 decoded value server side is also correct now while the usage of FileReader.readAsArrayBuffer() is recommended.

codekandis
  • 712
  • 1
  • 11
  • 22