Search code examples
md5scala.jswebjars

Generate md5 checksum scala js


I am trying to calculate hex md5 checksum at in scala js incrementally. The checksum will be verified at server side once file is transferred.

I tried using spark-md5 scala js web jar dependency:

libraryDependencies ++= Seq("org.webjars.npm" % "spark-md5" % "2.0.2")

jsDependencies += "org.webjars.npm" % "spark-md5" % "2.0.2" / "spark-md5.js"

scala js Code:-

val reader =  new FileReader
reader.readAsArrayBuffer(data)  // data is javascript blob object
val spark = scala.scalajs.js.Dynamic.global.SparkMD5.ArrayBuffer
reader.onload = (e: Event) => {
   spark.prototype.append(e.target)
   print("Checksum - > " + spark.end)
}

Error:-

Uncaught TypeError: Cannot read property 'buffer' of undefined at Object.SparkMD5.ArrayBuffer.append (sampleapp-jsdeps.js:596) at FileReader. (SampleApp.scala:458)

I tried google but most of the help is available are for javascript, couldn't find anything on how to use this library in scala js.

Sorry If I missed something very obvious, I am new to both javascript & scala js.


Solution

  • From spark-md5 readme, I read:

    var spark = new SparkMD5.ArrayBuffer();
    spark.append(e.target.result);
    var hexHash = spark.end();
    

    The way you translate that in Scala.js is as follows (assuming you want to do it the dynamically typed way):

    import scala.scalajs.js
    import scala.scalajs.js.typedarray._
    import org.scalajs.dom.{FileReader, Event}
    
    val SparkMD5 = js.Dynamic.global.SparkMD5
    val spark = js.Dynamic.newInstance(SparkMD5.ArrayBuffer)()
    val fileContent = e.target.asInstanceOf[FileReader].result.asInstanceOf[ArrayBuffer]
    spark.append(fileContent)
    val hexHashDyn = spark.end()
    val hexHash = hexHashDyn.asInstanceOf[String]
    

    Integrating that with your code snippet yields:

    val reader =  new FileReader
    reader.readAsArrayBuffer(data)  // data is javascript blob object
    val SparkMD5 = js.Dynamic.global.SparkMD5
    val spark = js.Dynamic.newInstance(SparkMD5)()
    reader.onload = (e: Event) => {
       val fileContent = e.target.asInstanceOf[FileReader].result.asInstanceOf[ArrayBuffer]
       spark.append(fileContent)
       print("Checksum - > " + spark.end().asInstanceOf[String])
    }
    

    If that's the only use of SparkMD5 in your codebase, you can stop there. If you plan to use it several times, you should probably define a facade type for the APIs you want to use:

    import scala.scalajs.js.annotation._
    
    @js.native
    object SparkMD5 extends js.Object {
      @js.native
      class ArrayBuffer() extends js.Object {
        def append(chunk: js.typedarray.ArrayBuffer): Unit = js.native
        def end(raw: Boolean = false): String = js.native
      }
    }
    

    which you can then use much more naturally as:

    val reader =  new FileReader
    reader.readAsArrayBuffer(data)  // data is javascript blob object
    val spark = new SparkMD5.ArrayBuffer()
    reader.onload = (e: Event) => {
       val fileContent = e.target.asInstanceOf[FileReader].result.asInstanceOf[ArrayBuffer]
       spark.append(fileContent)
       print("Checksum - > " + spark.end())
    }
    

    Disclaimer: not tested. It might need small adaptations here and there.