Home > Software engineering >  Client md5 (js) not matching server md5 (php) of image
Client md5 (js) not matching server md5 (php) of image

Time:11-05

In my client, a user selects a photo for upload, we do a small transformation on it using the canvas to resize it to a max height/width (preserving aspect ratio) of 2500 pixels. Once the user submits, the server writes it to a file and responds with the file md5 hash using md5_file. The md5 on the client is not matching what is returned by the server and I'm not sure why...

The JS library to generate the md5 is crypto-js, we're using jquery so expect to see $ and jquery methods on html elements.

The client code is:


/** This accepts the file from either the file input or a paste event, then resizes it 
using the canvas to a max of 2500x2500px while maintaing aspect ratio. The base64 data is 
then set at the img src attribute and then an html element is added to the imageContainer */
function handleImage(file, imageContainer) {
    const reader = new FileReader();
    reader.onload = function(event) {
        const img = new Image();
        img.src = event.target.result;
        img.onload = function() {
            const maxDimension = 2500;
            let width = img.width;
            let height = img.height;

            // Resize the image if it exceeds the maximum dimensions
            if (width > maxDimension || height > maxDimension) {
                if (width > height) {
                    width = maxDimension;
                    height = (maxDimension * img.height) / img.width;
                } else {
                    height = maxDimension;
                    width = (maxDimension * img.width) / img.height;
                }
            }

            // Create a canvas element to draw the resized image
            const canvas = document.createElement('canvas');
            canvas.width = width;
            canvas.height = height;
            const ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0, width, height);

            // Convert the canvas to a data URL
            const resizedDataUrl = canvas.toDataURL(file.type);

            const imageType = file.type.split("/")[1];
            const imagePreview = $("<div class='image-preview'><div class='loadingIcon' style='display:none'></div><img src='"   resizedDataUrl   "' data-type='"   imageType   "'><span class='remove-image'><i class='far fa-times'></i></span></div>");

            imageContainer.append(imagePreview);

            imagePreview.find(".remove-image").click(function() {
                imagePreview.remove();
            });
        };
    };
    reader.readAsDataURL(file);
}

...

/** Called during the submit event, grabs the base64 image data and the filetype, generates an md5 using only the image data, and then sends to the serve. */
function uploadImages(img) {
    const imgData = img.attr('src')
    const imgHash = CryptoJS.MD5(imgData.replace(/^data:image\/(\w*);base64,/, ""));
    const imgType = img.data('type')
    $.ajax({
        url: 'files',
        data: { file: imgData, fileType: imgType},
        method: "POST",
        dataType: "JSON",
        success: function (r) {
            container.find('.loadingIcon').hide();
            // TODO: These aren't matching, need to figure out why...
            console.log(r.md5, imgHash.toString())
        }
    })
}

The server code is:

<?
    // this accepts the file data from the $_POST body, strips out the base64 header, and 
    // then writes the file. Then uses the md5_file function to generate the md5 and 
    // echos it back.
    $file = $_POST['file'];
    $fileType = $_POST['fileType'] ?? 'jpg';
    $targetPath = "/uploads/files/" . uniqid() . "." . $fileType;;
    $data = explode( ',', $file )[1];  // strip out the base64 header
    $imageData = base64_decode($data);
    file_put_contents($targetPath, $imageData);
    echo md5_file($targetPath);
?>

Note that the code is simplified for brevity.

But the md5 I generate on the client is not matching the md5 returned by the server.

Does anyone see what I am doing wrong?

Thanks!

CodePudding user response:

The code in JS does the MD5 of a base64 string:

const imgHash = CryptoJS.MD5(imgData.replace(/^data:image\/(\w*);base64,/, ""));

But the code in PHP does not:

$data = explode( ',', $file )[1];  // strip out the base64 header
$imageData = base64_decode($data);
file_put_contents($targetPath, $imageData);
echo md5_file($targetPath);

Try doing:

echo md5($imageData);

CodePudding user response:

In js you are only hashing a base64 string while in php you are hashing a real file. These 2 things are completely different so it makes sense. if you want it to be the same you have to hash the buffer on js

  • Related