worker.ts 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import loadBlake3 from 'blake3/browser-async'
  2. import loadBlake3Wasm from 'blake3/dist/wasm/web/blake3_js_bg.wasm'
  3. import { encode as encodeHash, toB58String } from 'multihashes'
  4. type Blake3 = Awaited<ReturnType<typeof loadBlake3>>
  5. let blake3: Blake3
  6. const CHUNK_SIZE = 1024 * 1024 * 128
  7. export type HashingRequest = {
  8. hashingId: string
  9. file: File | Blob
  10. }
  11. export type HashingResponse = {
  12. hashingId: string
  13. hash: string
  14. }
  15. const createIterableFile = (file: File | Blob): AsyncIterable<Uint8Array> => {
  16. async function* iterator(): AsyncIterator<Uint8Array> {
  17. let offset = 0
  18. let fileSlice
  19. let result
  20. while (offset < file.size) {
  21. fileSlice = file.slice(offset, CHUNK_SIZE + offset)
  22. offset += CHUNK_SIZE
  23. result = await fileSlice.arrayBuffer()
  24. yield new Uint8Array(result)
  25. }
  26. }
  27. return {
  28. [Symbol.asyncIterator]: iterator,
  29. }
  30. }
  31. const getBlake3 = async (): Promise<Blake3> => {
  32. if (blake3) return blake3
  33. const blake3Init = async (imports: unknown) => {
  34. // @ts-ignore library provides typing for the wasm module but Vite WASM helper returns something else
  35. const exports = await loadBlake3Wasm(imports)
  36. return {
  37. instance: { exports },
  38. module: {},
  39. }
  40. }
  41. blake3 = await loadBlake3(blake3Init)
  42. return blake3
  43. }
  44. const computeFileHash = async (file: File | Blob): Promise<string> => {
  45. const { createHash } = await getBlake3()
  46. const hash = createHash()
  47. const iterableFile = createIterableFile(file)
  48. for await (const chunk of iterableFile) {
  49. hash.update(chunk)
  50. }
  51. const digest = hash.digest()
  52. return toB58String(encodeHash(digest, 'blake3'))
  53. }
  54. onmessage = async ({ data }) => {
  55. const hashingRequest = data as HashingRequest
  56. const hash = await computeFileHash(data.file)
  57. const response: HashingResponse = {
  58. hashingId: hashingRequest.hashingId,
  59. hash: hash,
  60. }
  61. postMessage(response)
  62. }