React-Native, Google Photos API Image Upload

3.1k Views Asked by At

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 })
    })
  }
3

There are 3 best solutions below

4
On BEST ANSWER

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

1
On

I've been trying the same thing in a Node Express app.
Don't forget that you need to upload the image in binary, and not in base64.
It also looks like you don't pass a parameter in step 4 to the function getUploadToken().

My app also returns the "message": "NOT_IMAGE: There was an error while trying to create this media item.".

In my node app i convert the Buffer to a binary with

const fileBinary = file.data.toString('binary')

1
On

Step 5 certainly does not work... You are not sending an image at all!

In step 4 you call this:

this.getUploadToken()

While in step 5

getUploadToken = (image) => {

image is then used as body.

Did you check your network tab while testing? It looks like you would be receiving a 400 error.