2

My intent:

I want my app to upload images to S3. If image already exists, server should record a reference to existing image rather than asking for an upload of another copy.

How I imagine that works:

  1. Hash image data
  2. Send hash to server with request for signed url (to upload to AWS S3)
  3. If hash matches something already stored, reference it and tell app

Initial thoughts:

Use imageEditor.cropImage to get image into ImageStore, which will give me an appropriate uri. Then use getBase64ForTag(uri, success, failure) to retrieve base64 data for a hash calculation.

The problem:

According to the answer on this question, this process is not efficient in the least. The usual solution would be to use native methods, as described in the answer to this question, however I do not want to eject my Expo app for this feature.

My Question:

Is there a better way to hash image data? Or more fundamentally, is there a better way of ensuring that identical images are not duplicated in S3 storage?

Community
  • 1
  • 1
Paul Parker
  • 1,163
  • 12
  • 23

2 Answers2

3

EDIT 2020-10-21 :

The library updated itself, and you should now call:

_hashImage = async (imageUri) => {
    return await FileSystem.getInfoAsync(imageUri, { md5: true } );
}

ORIGINAL:

It turns out that Expo provides this out of the box.

Expo.FileSystem.getInfoAsync

myImageHashFunction = async (imageUri) => {
  let fsInfo = await Expo.FileSystem.getInfoAsync(imageUri, [{ md5: true }] )
  console.log(fsInfo.md5)
}
PoulsQ
  • 1,936
  • 1
  • 15
  • 22
Paul Parker
  • 1,163
  • 12
  • 23
  • I'm using this and getting `[Unhandled promise rejection: Error: An exception was thrown while calling `ExponentFileSystem.getInfoAsync` with arguments `(]`. Any Idea why? – Abhishek Chandran Apr 05 '20 at 07:57
  • The only thing I can imagine going wrong is perhaps an incorrect imageUri. Where are you getting your Uri from? – Paul Parker Apr 05 '20 at 13:30
  • I'm getting Uri from the local storage of my ios device. Anyway, I got this working by converting it into base64 and then creating a hash of it. – Abhishek Chandran Apr 09 '20 at 17:56
0

If you are still looking for a solution:

This is how I got it working - create a base64 of the image and then create a hash of it.

import * as FileSystem from 'expo-file-system';
import * as Crypto from 'expo-crypto';

let info = await FileSystem.readAsStringAsync(imageUri, 
   { encoding: FileSystem.EncodingType.Base64 });

const hashData = await Crypto.digestStringAsync (
    Crypto.CryptoDigestAlgorithm.MD5,
    info
 )
Abhishek Chandran
  • 1,536
  • 1
  • 13
  • 21
  • This is the first method I tried. Unfortunately, it's very expensive to get the Base64 data across the JS-Native bridge. I can't imagine that's changed in the last two years. – Paul Parker Apr 12 '20 at 23:46
  • I tried doing the same but the hash generated by this process is not same as the one returned by the Mac? Do you have an idea this could be happening? – Tanya Sah Mar 12 '21 at 18:43
  • @TanyaSah it depends on the encoding. If you use different encodings to map strings to byte sequences, then the same input would give you two different hash values. – Abhishek Chandran Mar 15 '21 at 13:48
  • @Abhishek does expo includes the files metadata as well for calculating hash. Is it possible to get the hash with just the image data without any meta values? – Tanya Sah Mar 15 '21 at 13:55