0

The most closest thread is

https://stackoverflow.com/questions/60160794/getting-the-provided-image-is-in-an-unsupported-format-error-when-trying-to-in

But I don't want to open the image sharing it to the rest of world. So I configure with domain

file_id = uploaded_file.get('id')
drive_service.permissions().create(fileId=file_id, body={
    'type': 'domain',
    'domain': 'mydomain.com', # this is the domain I use the gsuite user e.g. richard@mydomain.com to login
    'role': 'reader'
}).execute()

And the the slides Resource is constructed by

def build_request(http, *args, **kwargs):
    import google_auth_httplib2
    new_http = google_auth_httplib2.AuthorizedHttp(credentials=drive_creds)
    auth_header = {
        'Authorization': 'Bearer '+ drive_creds.token
    }
    headers = kwargs.get('headers', {})
    if not headers:
        kwargs['headers'] = auth_headers
    else:
        kwargs['headers'].update(auth_header)
    http_req = googleapiclient.http.HttpRequest(new_http, *args, **kwargs)
    return http_req
slides_service = build('slides', 'v1', credentials=slides_creds, requestBuilder=build_request)

When executing I can observe that kwargs are passed to HttpRequest with Authorization fields configured correctly like

{'headers': {'Authorization': 'Bearer <google drive token>', ... }

However during execution, the create image function (I am sure my create image function works correctly because once I use public accessable image e.g. google logo, there is no problem posting the image to google slide page) always returns status code 400 with the message 'The provided image is in an unsupported format'. I open a private window and paste the link, it looks like it's still redirected the request to sign in page.

Any additional steps I need to configure to get this work? Many thanks for help.

Update 1:

Code below is used to create the corresponded slide and drive Resourc based on the google doc.

slides_scopes = ['https://www.googleapis.com/auth/drive',
                 'https://www.googleapis.com/auth/drive.file',
                 'https://www.googleapis.com/auth/drive.readonly',
                 'https://www.googleapis.com/auth/presentations',
                 'https://www.googleapis.com/auth/spreadsheets',
                 'https://www.googleapis.com/auth/spreadsheets.readonly']

drive_scopes =  ['https://www.googleapis.com/auth/drive',
                 'https://www.googleapis.com/auth/drive.appdata',
                 'https://www.googleapis.com/auth/drive.file',
                 'https://www.googleapis.com/auth/drive.metadata',
                 'https://www.googleapis.com/auth/drive.metadata.readonly',
                 'https://www.googleapis.com/auth/drive.photos.readonly',
                 'https://www.googleapis.com/auth/drive.readonly']


def auth(token_file='token.pickle', credentials_json='cred.json',scopes=[]):

    creds = None
    if os.path.exists(token_file):
        with open(token_file, 'rb') as token:
            creds = pickle.load(token)
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                credentials_json, scopes)
            creds = flow.run_local_server(port=0)
        with open(token_file, 'wb') as token:
            pickle.dump(creds, token)
    return creds

slides_creds = auth(token_file='slides_t.pickle', credentials_json='slides_t.json', scopes=slides_scopes)
drive_creds = auth(token_file='drive_t.pickle', credentials_json='drive_t.json', scopes=drive_scopes)
def build_request(http, *args, **kwargs):
    import google_auth_httplib2
    new_http = google_auth_httplib2.AuthorizedHttp(credentials=drive_creds)
    auth_header = {
        'Authorization': 'Bearer '+ drive_creds.token
    }
    headers = kwargs.get('headers', {})
    if not headers:
        kwargs['headers'] = auth_headers
    else:
        kwargs['headers'].update(auth_header)
    http_req = googleapiclient.http.HttpRequest(new_http, *args, **kwargs)
    return http_req
slides_service = build('slides', 'v1', credentials=slides_creds, requestBuilder=build_request)
drive_service = build('drive', 'v3', credentials=drive_creds)

Update 2:

When switching to serve image using local http server (it's accesiable with http://<intranet ip or localhost>:<port>/path/to/img.png, google slide api returns following error

"Invalid requests[0].createImage: There was a problem retrieving the image. The provided image should be publicly accessible, within size limit, and in supported formats."

This makes me wonder perhaps google slide API no longer allows accessing webContentLink with special perrmission (e.g. domain). Only public accessible url is allowed instead.

Update 3:

  • create_image function parameters:

    • slide_object_id: g6f1c6c22f2_1_69

    • web_content_link: https://drive.google.com/a/{{domain name}}/uc?id={{image id}}&export=download

    • size: {'height': {'magnitude': 10800, 'unit': 'EMU'}, 'width': {'magnitude': 19800, 'unit': 'EMU'}}

    • transform: {'scaleY': 171.9097, 'scaleX': 212.4558, 'translateY': 937125, 'translateX': 2347875, 'unit': 'EMU'}

  • create image function

    def create_image(slides_service=None, slide_object_id=None, web_content_link=None, size_height_magnitude=4000000, size_width_magnitude=4000000,  transform_scale_x=1,  transform_scale_y=1, transform_translate_x=100000,  transform_translate_y=100000):
        requests = []
        requests.append({
            'createImage': {
                'url': web_content_link,
                'elementProperties': {
                    'pageObjectId': slide_object_id,
                    'size': {
                        'height': {
                            'magnitude': size_height_magnitude,
                            'unit': 'EMU'
                        },
                        'width': {
                            'magnitude': size_width_magnitude,
                            'unit': 'EMU'
                        }
                    },
                    'transform': {
                        'scaleX': transform_scale_x,
                        'scaleY': transform_scale_y,
                        'translateX': transform_translate_x,
                        'translateY': transform_translate_y,
                        'unit': 'EMU'
                    }
                }
            }
        })
        body = {
            'requests': requests
        }
        response = slides_service.presentations() \
            .batchUpdate(presentationId=presentation_id, body=body).execute()
        return response.get('replies')[0].get('createImage')
    
  • Could you share the code related to the request you are making with `slides_service`? – Iamblichus Apr 09 '20 at 07:18
  • @Iamblichus Sorry replying late. I update the code in the update section. Basically the code follows (copy/ paste with some modification) the google doc w/t any significant change. – Richard Jackson Apr 13 '20 at 16:14
  • What I meant is if you could share the API call itself, meaning the body and the parameters you provide to the request, as well as the method you are using. I guess you are making a [batchUpdate: createImage](https://developers.google.com/slides/reference/rest/v1/presentations/request#createimagerequest), but it would be useful to know what parameters and body you are providing. I cannot see that in the code you provided. – Iamblichus Apr 14 '20 at 09:27
  • The parameters passed to createImage API and the function are in Update 3 section. Please let me know if more information is needed. – Richard Jackson Apr 14 '20 at 16:14

1 Answers1

0

Issue:

Only images that have a publicly accessible URL can be added to a slide. Sharing the image with the account from which the request is made is not enough. As you can see in the official documentation:

If you want to add private or local images to a slide, you'll first need to make them available on a publicly accessible URL.

Workarounds:

  • What you could do, as explained in Tanaike's answer, is to: (1) share the image publicly, (2) add the image to the slide using the publicly accessible URL, and (3) delete the Permission created in step 1. This way, the image is publicly accessible only during the short time the program takes to execute steps 2 and 3.

  • Another option, provided in the referenced documentation, is to:

upload your images to Google Cloud Storage and use signed URLs with a 15 minute TTL. Uploaded images are automatically deleted after 15 minutes.

Reference:

Iamblichus
  • 18,540
  • 2
  • 11
  • 27
  • Thank for the information. That was how I tested if Google slides' adding image function was working or not (temporarily allow public accisable url and then remove that permission with Google logo for instatnce); and it worked without a problem. But this really makes me wonder if a user who has the requirement that the images can not be shared, the only option left is to temporarily make the image publicly accessible and then remove that permission - this seems to me such operation would just increase risk (business information leak). Anyway thanks for the information. – Richard Jackson Apr 23 '20 at 07:21
  • @RichardJackson you're welcome. Also, remember that you can [accept](https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work) answers that you consider helpful. This is useful because other users might face this issue. StackOverflow relies on this to share knowledge to other users. – Iamblichus Apr 23 '20 at 09:07