You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Google API向Slides插入图片时频繁触发权限错误的问题

解决Google Slides API替换图片时的权限禁止错误

问题背景

我使用Python3通过Google Slides API自动生成幻灯片,需将图表上传至指定幻灯片。已创建启用Slides和Drive API的服务账号,密钥文件为secret.json。

当前流程:模板幻灯片的文本框含{{tag_name}}标签用于替换文本,矩形形状含{{graph_name}}标签用于替换图片。代码执行步骤为:复制模板幻灯片到目标文件夹→上传图片至该文件夹→替换形状为图片。图片要求可被任何人通过链接查看和下载,手动上传并设置权限时可正常工作,但在Jupyter Notebook中通过API操作时,替换形状为图片的代码单元通常会抛出错误,偶尔重新运行相同或不同图片的代码单元却能成功。

报错信息

return (<HttpError 400 when requesting https://slides.googleapis.com/v1/presentations/1yv3qJImZYBONjhOvBWgmYUf7PYaaFHpzWdoMWG7ZE_o:batchUpdate?alt=json returned "Invalid requests[0].replaceAllShapesWithImage: Access to the provided image was forbidden.". Details: "Invalid requests[0].replaceAllShapesWithImage: Access to the provided image was forbidden.">
)

我无法确定偶尔成功的原因,怀疑可能与在Safari浏览器中查看生成内容时的操作有关,或许某些操作偶然修复了认证问题?运行设置图片权限的代码后,在Safari中可确认权限已设为“任何人可通过链接访问”。尝试过调整权限参数,但问题仍存在。

原代码

from google_auth_httplib2 import httplib2
from apiclient import discovery
from oauth2client.service_account import ServiceAccountCredentials
from googleapiclient.http import MediaFileUpload

TEMPLATE_ID = '1JVuNOJxqajBxTkdASgnbJkiQLZtt9F1E-yCAaOU1fPQ'
DEST_FOLDER_ID = '1pVakIO5ZlF1HRi7XJFM1D4llajU9pGqs'


def get_service(secrets_path, service_scope, service_build, service_version):
    credentials = ServiceAccountCredentials.from_json_keyfile_name(
        secrets_path, 
        scopes= service_scope
    )
    service = discovery.build(
        service_build,
        service_version,
        http=credentials.authorize(httplib2.Http()),
        cache_discovery=False
    )
    return service

def replace_shape_with_image(url: str, presentation_id: str, slides_service, contains_text: str=None, pages = []):
    slides_service.presentations().batchUpdate(
        body={
            "requests": [
                {
                    "replaceAllShapesWithImage": {
                        "imageUrl": url,
                        "replaceMethod": "CENTER_INSIDE",
                        "containsText": {
                            "text": "{{" + contains_text + "}}",
                        },
                        "pageObjectIds": pages
                    }
                }
            ]
        },
        presentationId=presentation_id
    ).execute()

# Get api services
drive_service = get_service(
            './secret.json',
            'https://www.googleapis.com/auth/drive',
            'drive',
            'v3'
        )
slides_service =  get_service(
            "./secret.json",
            ['https://www.googleapis.com/auth/presentations','https://www.googleapis.com/auth/drive'],
            'slides',
            'v1'
        )

# Create slideshow from copy
r = drive_service.files().copy(
        fileId=TEMPLATE_ID,
        body={'name': 'Slideshow', 'parents': DEST_FOLDER_ID},
        supportsTeamDrives=True).execute()
file_id = r.get('id')

# Get the slide ID's
r = slides_service.presentations().get(
    presentationId=file_id,
    fields='slides(objectId)'
).execute()

slides = [s['objectId'] for s in r['slides']]
slide_with_graph_placeholder = slides[-1]

# Upload a graph .png file to the folder
file_metadata = {
            'name': 'photo.png',
            'parents': [DEST_FOLDER_ID]
        }
media = MediaFileUpload('./path/to/image.png',
                        mimetype='image/png', resumable=True)
file = drive_service.files().create(body=file_metadata, media_body=media, fields='*').execute()

#.Update file permissions for the image file to be anyone with link
drive_service.permissions().create(
    fileId=file.get("id"),
    body={
    'type': 'anyone',
    'value': 'anyone',
    'kind': 'drive#permission',
    'role': 'reader'
    }
    ).execute()

# This is the function call that throws the exception 
try:
    replace_shape_with_image(
        url=file.get('webContentLink'),
        presentation_id=file_id,
        slides_service=slides_service,
        contains_text='graph',
        pages=[slide_with_graph_placeholder]
    )
except Exception as e:
    print(e)

解决方案

1. 使用图片的直接访问URL

webContentLink是网页跳转链接,Slides API无法直接解析该链接获取图片资源。应该构造直接的下载URL:

image_url = f"https://drive.google.com/uc?id={file.get('id')}&export=download"

2. 添加权限生效延迟

权限设置是异步操作,可能还未完全生效就调用了Slides API。添加短暂延迟确保权限生效:

import time
# 设置权限后添加延迟
time.sleep(2)

3. 验证服务账号权限范围

确保Slides服务的权限包含https://www.googleapis.com/auth/drive,保证服务账号能访问Drive中的图片文件。

修改后的关键代码片段

import time

# ... 其他代码不变 ...

#.Update file permissions for the image file to be anyone with link
drive_service.permissions().create(
    fileId=file.get("id"),
    body={
    'type': 'anyone',
    'value': 'anyone',
    'kind': 'drive#permission',
    'role': 'reader'
    }
    ).execute()

# 添加延迟确保权限生效
time.sleep(2)

# 使用直接访问URL替换图片
try:
    image_url = f"https://drive.google.com/uc?id={file.get('id')}&export=download"
    replace_shape_with_image(
        url=image_url,
        presentation_id=file_id,
        slides_service=slides_service,
        contains_text='graph',
        pages=[slide_with_graph_placeholder]
    )
except Exception as e:
    print(e)

内容的提问来源于stack exchange,提问作者thecreamybeige

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 09:59:58