使用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
相关产品推荐
相关产品推荐

