0

I was struggling to create a JavaScript app to generate checksums on remote images (using URLs)

My program is using checksums to detect when my images on the remote server are changing (though their names do not change : for example , image0 remains image0 even though the image itself has changed) and then inform the client user.

I succeeded in coding a Python script that saves the checksums in a text file, now I would like to compare these 'old' checksums with newer ones using JavaScript on the HTML webpage of the client.

Here is a very interesting link that sums up what I would like to achieve https://codepen.io/dlanuara/pen/mLRLpL - ideally I would like to automatise the process and execute the checksum generator without the user input (use something different from the function uploadFile(file) {} (Javascript is relatively new to me so I couldn't figure out how to change the code there, but I'm sure there are possibilities)

Here is a question IDENTICAL to mine, which did not help me solve my problem ;/// How can I get an MD5 checksum of an image at a specific URL in Javascript?

Many thanks for your help !

unrealapex
  • 578
  • 9
  • 23

1 Answers1

0

This only partially answers your question, specifically the part on how to get the image from the remote server and on how one could compute a hash sum from this image.

For simplicity/ availability here I am using the crypto Web API (specifically crypot.subtle.digest()) which does not support creating MD5 hashes. But the only thing you will have to change is the hash algorithm if you really need an MD5 hash. There are libraries like CryptoJS out there that can do that. There are also tons of open source implementations if you want to have a look (Also see this SO answer).

To get the image you can use the Fetch API. Then get the raw image using arrayBuffer(), compute the hash and (if required) convert the hash back to a hex string which is the typical format hashes are usually displayed.

(async() => {
    // get image from remote server
    const imageResponse = await fetch("https://upload.wikimedia.org/wikipedia/commons/b/b6/Image_created_with_a_mobile_phone.png");
    if(imageResponse.status !== 200){
      throw new Error(`Failed to get image with status code ${imageResponse.status}`); 
    }
    const image = await imageResponse.arrayBuffer(); // get image bytes
    // if you need/ want to use MD5 you cannot use the crypto API
    // Instead use a library or write the MD5 hash function yourself (there al)
    const hashBuffer = await crypto.subtle.digest("SHA-1", image); // compute hash
    const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
    console.log(convertToHexString(hashArray)); // convert to human-readable hex-string
})();

function convertToHexString(hashArray){
  return hashArray
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
}

The rest of your task should be as simple as comparing two strings. If they match, the image is (very likely) the same. If they don't, they're not.

crypto.subtle obviously only works for compatible browsers and in a secure context meaning you need to use HTTPS (not HTTP!).

Mushroomator
  • 6,516
  • 1
  • 10
  • 27
  • Thanks ! At least it solves part of my problem. MD5 is not necessary. I just need something that keeps me aware when my images are changing. How would I implement that in a js script ? I tried to invoke the async function but I get something weird : `Uncaught ReferenceError: async is not defined`. Am I doing something wrong there ? – chipmunktunic Apr 04 '23 at 09:41
  • Might be an issue with the [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE). I just used that here as otherwise you get an error that you cannot have top-level `async` functions here. Might also be realted to [this](https://stackoverflow.com/questions/52336967/referenceerror-with-async-function). – Mushroomator Apr 04 '23 at 09:56
  • is `async();` the right way to call the module ? Maybe the way I am calling it is causing the issue – chipmunktunic Apr 04 '23 at 10:32
  • No, that's not the right way when you use exactly what I've written. An IIFE does not have a name therefore you cannot call it like this. It is immediately invoked (hence the name) and you don't need to call it separetly. Notice the parenthesis `(async () => {...})()` at the end, like a normal function invokation. Think of it as declaring and at the same time executing the function. You should however create a named function e.g `getRemoteImageHash()`and put the code within the IIFE in the function body and then invoke that function using `await getRemoteImageHash()`. – Mushroomator Apr 04 '23 at 10:51
  • Thanks ! I knew this thing was faulty.So I tried with `let getHash = async() => {};` but I got a new error related to the crypto module : `Uncaught (in promise) TypeError: crypto.subtle is undefined` – chipmunktunic Apr 04 '23 at 11:23
  • For `crypto.subtle` to be able to work you need to have a [compatible browser](https://developer.mozilla.org/en-US/docs/Web/API/Crypto#browser_compatibility) and a secure context which means you have to use HTTPS instead of HTTP. – Mushroomator Apr 04 '23 at 14:14