0

I am trying to insert an image (PNG) in a Google Slide presentation using the Slides API. I do this by first uploading the image to the user's Drive, obtaining the url, passing that along to the Slide API via the correct request and then deleting the image file.

What used to work as of a few weeks ago:

image_url = '%s&access_token=%s' % (
          drive_service.files().get_media(fileId=image_file_id).uri,
          creds.token)

However, there have been changes to the Drive API, such that URLS constructed this way no longer work.

I am having difficulty figuring out the new correct URL to use here. The options as per the doc that describes the change are:

  1. Use webContentLink -- Downloads
  2. Use webViewLink -- View
  3. Use exportLinks -- Export

I use code that looks like this to get these links:

 upload = drive_service.files().create(
          body={'name': 'My Image File'},
          media_body=media_body,
          fields='webContentLink, id, webViewLink').execute()
 image_url = upload.get('webContentLink')

I have tried both #1 and #2 and get the following error:

"Invalid requests[0].createImage: The provided image is in an unsupported format."

I have also been receiving the following error intermittently: "Invalid requests[0].createImage: Access to the provided image was forbidden."

I verified that I am able to download / view the image from the URLs generated in #1 and #2. I didn't try #3 since I am not trying to export to a different format.

What would be the best way to go about figuring out the correct URL to use?

Karthik V
  • 1,867
  • 1
  • 16
  • 23
  • Sometimes this weird error happens to png files. Can you try run same code on jpg image and check error still happens? – Kos Feb 11 '20 at 01:54

2 Answers2

3

From your script, I think that the reason of your issue is due to this. By this, the query parameter of access_token cannot be used. Under this situation, when image_url = '%s&access_token=%s' % (drive_service.files().get_media(fileId=image_file_id).uri,creds.token) is used, the login page is returned. By this, such error occurs. So as a workaround, how about the following flow?

Flow:

  1. Upload a PNG file.
  2. Publicly share the PNG file by creating a permission.
  3. Insert the PNG file to Slides.
  4. Close the shared PNG file by deleting the permission.

When the image file is put to the Slides, even when the permission of file is deleted, the image is not removed from the Slides. This workaround uses this.

Sample script:

For above flow, the sample script of python is as follows. Please set the variables of uploadFilename, presentation_id and pageObjectId

uploadFilename = './sample.png'  # Please set the filename with the path.
presentation_id = '###'  # Please set the Google Slides ID.
pageObjectId = '###'  # Please set the page ID of the Slides.

drive = build('drive', 'v3', credentials=creds)
slides = build('slides', 'v1', credentials=creds)

# 1. Upload a PNG file from local PC
file_metadata = {'name': uploadFilename}
media = MediaFileUpload(uploadFilename, mimetype='image/png')
upload = drive.files().create(body=file_metadata, media_body=media, fields='webContentLink, id, webViewLink').execute()
fileId = upload.get('id')
url = upload.get('webContentLink')

# 2. Share publicly the uploaded PNG file by creating permissions.
drive.permissions().create(fileId=fileId, body={'type': 'anyone', 'role': 'reader'}).execute()

# 3. Insert the PNG file to the Slides.
body = {
    "requests": [
        {
            "createImage": {
                "url": url,
                "elementProperties": {
                    "pageObjectId": pageObjectId
                }
            }
        }
    ]
}
slides.presentations().batchUpdate(presentationId=presentation_id, body=body).execute()

# 4. Delete the permissions. By this, the shared PNG file is closed.
drive.permissions().delete(fileId=fileId, permissionId='anyoneWithLink').execute()

Note:

  • I thought that from your script, you might be using google-api-python-client with python. So I proposed the sample script for python.
  • In this case, the scopes for using Slides API and Drive API are required. Please be careful this.
  • In the case of Google Apps Script, you can see the sample script at here.

References:

If I misunderstood your question and this was not the direction you want, I apologize.

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • Thanks for the detailed reply! When I try to change permissions, I get an Http 403 error: `"The user does not have sufficient permissions for this file."`. This is a bit strange because I have the auth/drive scope, and I am able to create the file just fine and access it using the various urls (webView, webContent etc.,) – Karthik V Feb 11 '20 at 18:57
  • @Karthik V Thank you for replying. I apologize for the inconvenience. I agree with you. In my environment, I could confirm that the above sample script works fine. So I proposed above script. About the error, when you upload the image file, can you confirm the owner of the file? If the owner is not you, such error might occur. In that case, how about copying the file as you which is the owner? But in this case, I cannot replicate your issue. So if this was not the direct solution of your issue, I apologize. – Tanaike Feb 11 '20 at 22:44
  • @Karthik V By the way, I'm not sure about your whole script. So can I ask you about the scopes you are using? And in your script, which are you using Service account or OAuth2? – Tanaike Feb 12 '20 at 00:01
  • @Tanaike -- this worked fore me the last 2 months but as of today it no longer works. Is this something that is still good on your end? Or did google change up things again? – Mike May 19 '20 at 21:45
  • @Mike Thank you for your comment. Unfortunately, from your comment, I cannot understand about the detail of `today it no longer works`. Can you provide the detail information about it? – Tanaike May 19 '20 at 23:12
  • the code above with step 1-4. It looks like the error no longer is relevant -- maybe google api side was messing up. The error I was seeing was: googleapiclient.errors.HttpError: ?alt=json returned "Invalid requests[152].createImage: Access to the provided image was forbidden."> but i just reran it (no changing of code at all) and it now works. – Mike May 19 '20 at 23:15
  • @Mike Thank you for replying. About `it now works.`, now the issue was resolved. Is my understanding correct? – Tanaike May 19 '20 at 23:18
  • yes -- thanks for the response. Every now and step 3 fails and replies with: googleapiclient.errors.HttpError: and if you wait long enough the code (steps 1-4 above) will work without changing a line of code. I am assuming the error has something to do with the google api side dealing with images in drive and replacing them in slides. – Mike May 20 '20 at 00:51
0

I was running into the same error even when using the flow involving granting temporary permissions access then removing the permissions after calling .createImage() or .replaceAllShapesWithImage()

I also ran into this error when creating permissions for a folder containing those images: "Invalid requests[0].replaceAllShapesWithImage: Access to the provided image was forbidden." Not sure why the permissions are not propagating to the images...

Following Kos' comment, switching to jpg file type worked for me.

Edit:

It appears I am also required to set the scope to 'https://www.googleapis.com/auth/drive' in order for it to work, which isn't ideal, but is sufficient for now.

Edit 2:

Nevermind it appears to be inconsistent. I am running into the permissions access error again. Deleting my token.pickle does not seem to fix either