Search code examples
react-nativebase64blobgoogle-photosoctetstring

React-Native, Google Photos API Image Upload


react-native + expo

TL;DR: Does anyone have a working example of react-native/expo working with an in app camera and uploading an image to the 'new' google api?

  1. Authenticate user (working)
  2. Make Album (working)
  3. Make Album Shareable (working)
  4. Take Photo (working)
  5. Get UploadToken (Seems like it is working)
  6. Upload Photo (not working)

Why I think (5) seems like it is working, because the getUploadToken function returns successfully, response 200, and provides a key.

Why I think it might be a dumb service on the other end of (5), I can post just about anything to it and it will return successfully.

My hunch is that there is something wrong with the way I am uploading the image to the /uploads endpoint.

IE: not in the correct format.

this.state.image == {'base64':'base64string','uri':'file://...',...}

I do see an album is being created within my google photos, and I can see that it is set to shareable, with no privileges (users can not comment, or add their own photos)

2 MAKE ALBUM

makeAlbum = () => {
    //MAKE ALBUM
    fetch('https://photoslibrary.googleapis.com/v1/albums', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer '+this.state.accessToken
      },
      body: JSON.stringify({
        "album": {"title": this.state.albumTemp}
      }),
    }).then((response) => response.json())
      .then((response) => {
        [
          this.shareAlbum(response),
          console.log('make: ',response)
        ]
    });
  }
}

3 MAKE ALBUM SHAREABLE

shareAlbum = (response) =>{
    this.setState({albumId:response.id})
    //MAKE ALBUM SHAREABLE
    fetch('https://photoslibrary.googleapis.com/v1/albums/'+this.state.albumId+':share',{
      method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer '+this.state.accessToken
        }
    }).then((response) => response.json())
      .then((response)=>{
        [
          this.setState({shareable:response.shareInfo.shareableUrl}),
          console.log('share1: ',this.state.shareable),
          console.log('share2: ',response),
        ]
    });
  }

4 CAPTURE PHOTO

capturePhoto = async () => {
    let image = await this._camera.takePictureAsync({
      base64: true,
      quality: .5,
      width: 1920,
      fixOrientation: true
    })
    this.setState({ image: image, capturing: false })
    // delay the capture a few seconds to make sure it's rendered
    setTimeout(async () => {
      let result = await takeSnapshotAsync(this._previewRef, {
        format: 'png',
        result: 'tmpfile',
        quality: 1
      });
      //upload the photo to google photos album
      this.getUploadToken(image)
    }, 1000)
  }

5 GET UPLOAD TOKEN

getUploadToken = (image) => {
    let name = this.state.image.uri.split('/')
    name=name[name.length-1]
    fetch('https://photoslibrary.googleapis.com/v1/uploads', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/octet-stream',
        'Authorization': 'Bearer '+this.state.accessToken,
        'X-Goog-Upload-File-Name': name,
        'X-Goog-Upload-Protocol': 'raw'
      },
      body:image,
    })
    .then((response) => {
      console.log('setup upload: ',response)
      this.uploadPhoto(response._bodyText); 
    })
  }

6 UPLOAD PHOTO

uploadPhoto = (token) => {
fetch('https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer '+this.state.accessToken,
      },
      body:JSON.stringify({
        "albumId": this.state.albumId,
        "newMediaItems": [
          {
            "description": "Event Photo",
            "simpleMediaItem": {
              "uploadToken": token
            }
          }
        ]
      })
    })
    .then((response) => {
      console.log('actual upload: ',response)
      this.setState({ ready: true, image: null })
    })
  }

Solution

  • 5 GET UPLOAD TOKEN API works find, just the description is wrong on Google Documentation. Instead of Base64 the input is in form of Binary. I have tried in Postman(below screenshot):

    Get Upload Token API: Get Upload Token API

    Upload Media: enter image description here