Armin
Armin

Reputation: 1281

Decode QR Code Image from Camera Roll React Native

I'm trying to implement a feature in my app where I the user can select a picture from their camera roll and the app will decode a QR code in the image is detected.

I'm currently using react-native-camera-roll-picker: https://github.com/jeanpan/react-native-camera-roll-picker and react-native-qrcode-local-image: https://github.com/remobile/react-native-qrcode-local-image

The problem is the local QR code image library wants me to pass a local path and isn't compatible with the native uri provided by react-native-camera-roll-picker. I would use another library for decoding the image QR code but this one appears to be the only one that works on iOS and Android and scans from existing images rather than from the actual camera.

I've also tried implementing react-native-fetch-blob in order to temporarily save the camera roll image locally, but that's been giving me trouble as well: https://github.com/wkh237/react-native-fetch-blob

This is my current attempt in a function that I call in the "callback" prop for react-native-camera-roll-picker (with previous attempts commented out):

_pickedImage(array,currentImg) {
console.log(currentImg)
var path = RNFS.DocumentDirectoryPath + '/pickedqr';
let rnfbURI = RNFetchBlob.wrap(RNFetchBlob.fs.asset(currentImg.uri))
const Blob = RNFetchBlob.polyfill.Blob
Blob.build(rnfbURI, {type:'image/jpg'}).then((b) => {
  tmpBlob = b;
  RNFetchBlob.fs.readFile(tmpBlob, 'base64').then((data) => {
    console.log("Base64", data)
    QRDecoder.decode(`data:image/gif;base64,${data}`, (error, result)=>{
      console.log("Code", result)
      console.log("Error", error)
    });
  });
})
/*fullPath = currentImg.uri.replace("assets-library://", "cdvfile://localhost/assets-library/")
QRDecoder.decode(fullPath, (error, result)=>{
  console.log("Code", result)
  console.log("Error", error)
});*/
/*let blb = Blob.build( rnfbURI, { type: 'image/jpg'})
console.log(blb)*/
/*RNFetchBlob.fs.readFile(rnfbURI, 'base64').then((data) => {
  console.log("Base64", data)
  QRDecoder.decode(`data:image/gif;base64,${data}`, (error, result)=>{
    console.log("Code", result)
    console.log("Error", error)
  });
})*/
}

I'm at a total loss at the moment so any methods or insight would be much appreciated.

Upvotes: 17

Views: 7174

Answers (5)

Gev
Gev

Reputation: 882

You can use rn-qr-generator

It can detect QR code in image


RNQRGenerator.detect({
  uri: PATH_TO_IMAGE, // local path of the image. Can be skipped if base64 is passed.
  base64: imageBase64String, // If uri is passed this option will be skipped.
})
  .then(response => {
    const { values } = response; // Array of detected QR code values. Empty if nothing found.
  })

Upvotes: 0

azamani
azamani

Reputation: 121

You can just use react-native-rn-zxing:

npm i react-native-rn-zxing

then link it :

react-native link react-native-rn-zxing

Usage :

import RnZxing from 'react-native-rn-zxing';
 
...
// Pass the callback as a parameter
RnZxing.showQrReader(this.onBarcodeScanned);
 
...
// Define the callback function to handle data after scan
onBarcodeScanned = (data) => {
        this.setState({data: data});
};

Upvotes: 1

Roman Pavlov
Roman Pavlov

Reputation: 119

This answer solved it for me. Created native method to covert uri in path, here is my code:

    @ReactMethod
    public void getRealPathFromURI(String contentUriString, Promise promise) {
        Uri contentUri = Uri.parse(contentUriString);
        Cursor cursor = null;
        try {
            String wholeID = DocumentsContract.getDocumentId(contentUri);
            String id = wholeID.split(":")[1];

            String[] column = { MediaStore.Images.Media.DATA };

            String sel = MediaStore.Images.Media._ID + "=?";

            cursor = reactContext.getContentResolver().
                    query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                            column, sel, new String[]{ id }, null);

            int columnIndex = cursor.getColumnIndex(column[0]);

            String filePath = "";
            if (cursor.moveToFirst()) {
                filePath = cursor.getString(columnIndex);
            }
            promise.resolve(filePath);
        } catch (Throwable e) {
            promise.reject(e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

And js:

DocumentPicker.pick({ type }).then((document: DocumentPickerResponse) => {
    MyNativeModule.getRealPathFromURI(document.uri).then((path) => {
        QRCodeLocalImage.decode(path, (err, res) => {
            callback(err, { qrData: res });
        });
    });
});

Upvotes: 1

Daniel Zheng
Daniel Zheng

Reputation: 312

You can use react-native-camera to solve this issue easily.

Here's the simple code snippet for that.

handleCodeDetected = (data) => {
// do whatever you want to do with data    
}

...
...

<RNCamera
  type={RNCamera.Constants.Type.back}
  barCodeTypes={[RNCamera.Constants.BarCodeType.qr]}
  onBarCodeRead={this.handleCodeDetected}
  style={styles.preview}
/>

Upvotes: 1

Shashin Bhayani
Shashin Bhayani

Reputation: 1576

you can use react-native-qrcode-scanner to scan QR from images or directly through the camera.

INSTALLATION:

install dependency by using this command:

yarn add react-native-camera react-native-qr-scanner

link those libraries by using:

react-native link react-native-camera && react-native-qr-scanner

you need to add the permission to your AndroidManifest.xml of your project. This should be found in your android/app/src/main/AndroidManifest.xml Add the following:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE"/>

With iOS 10 and higher, you need to add the "Privacy - Camera Usage Description" key to the info.plist of your project. This should be found in your_project/ios/your_project/Info.plist. Add the following code:

<key>NSCameraUsageDescription</key>
<string/>
<key>NSPhotoLibraryUsageDescription</key>
<string/>
<key>NSMicrophoneUsageDescription</key>
<string/>
<key>NSPhotoLibraryAddUsageDescription</key>
<string/>

Usage:

import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, TouchableOpacity} from 'react-native';
import {QRreader} from 'react-native-qr-scanner';
import ImagePicker from 'react-native-image-picker';
 
export default class Scanner extends Component {
  constructor(props) {
    super(props);
    this.state = {
      reader: {
        message: null,
        data: null
      }
    };
  }
  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity onPress={()=>{
          this.openPhoto();
        }}>
          <Text style={{marginTop: 20}}>打开相册识别二维码</Text>
        </TouchableOpacity>
        <View>
        {!this.state.reader? <Text>{!this.state.reader.message?'':`${this.state.reader.message}`}</Text>: <Text>{!this.state.reader.message?'':`${this.state.reader.message}:${this.state.reader.data}`}</Text>}
        </View>
      </View>
    );
  }
  
  openPhoto(){
    console.log('ImagePicker');
    ImagePicker.launchImageLibrary({}, (response) => {
      console.log('Response = ', response);
    
      if (response.didCancel) {
        console.log('User cancelled image picker');
      }
      else if (response.error) {
        console.log('ImagePicker Error: ', response.error);
      }
      else if (response.customButton) {
        console.log('User tapped custom button: ', response.customButton);
      }
      else {
        if(response.uri){
          var path = response.path;
          if(!path){
              path = response.uri;
          }
          QRreader(path).then((data)=>{
            this.setState({reader: {
              message: '识别成功',
              data: data
            }});
            // 十秒后自动清空
            setTimeout(() => {
              this.setState({reader: {
                message: null,
                data: null
              }})
            }, 10000);
          }).catch((err)=>{
            this.setState({reader: {
              message: '识别失败',
              data: null
            }});
          });
          
      }
      }
    });
  }
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  }
});

you can read more about this library here: https://www.npmjs.com/package/react-native-qr-scanner

Upvotes: 1

Related Questions