使用toUri()将Google Drive Picker嵌入自定义Div的iframe时遇403错误
Google Drive Picker嵌入自定义Div遭遇403权限错误
我正在开发Google Drive Picker,已基于谷歌官方示例代码创建选择器。需要将其嵌入自定义Div/模块中,因此使用Google API的.toUri()方法生成src链接,添加到iframe后嵌入自定义Div。虽然成功嵌入,但出现403错误:
- That’s an error.
We're sorry, but you do not have access to this page. That’s all we know.
错误截图:403错误截图
以下是完整实现代码:
<!DOCTYPE html> <html> <head> <title>Picker API Quickstart</title> <meta charset="utf-8" /> </head> <body> <p>Picker API API Quickstart</p> <!--Add buttons to initiate auth sequence and sign out--> <button id="authorize_button" onclick="handleAuthClick()">Authorize</button> <button id="signout_button" onclick="handleSignoutClick()">Sign Out</button> <pre id="content" style="white-space: pre-wrap;"></pre> <div id="pickerDiv" class="picker-dialog" style="height: 500px; width:800px; border: 1px solid red; position: relative; top: 50%;"></div> <script type="text/javascript"> const SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly'; const CLIENT_ID = '###########'; const API_KEY = '########'; const APP_ID = '######'; let tokenClient; let accessToken = null; let pickerInited = false; let gisInited = false; document.getElementById('authorize_button').style.visibility = 'hidden'; function gapiLoaded() { gapi.load('picker', intializePicker); } function intializePicker() { pickerInited = true; maybeEnableButtons(); } function gisLoaded() { tokenClient = google.accounts.oauth2.initTokenClient({ client_id: CLIENT_ID, scope: SCOPES, callback: '', // defined later }); gisInited = true; maybeEnableButtons(); } function maybeEnableButtons() { if (pickerInited && gisInited) { document.getElementById('authorize_button').style.visibility = 'visible'; } } function handleAuthClick() { tokenClient.callback = async (response) => { if (response.error !== undefined) { throw (response); } accessToken = response.access_token; document.getElementById('signout_button').style.visibility = 'visible'; document.getElementById('authorize_button').innerText = 'Refresh'; await createPicker(); }; if (accessToken === null) { tokenClient.requestAccessToken({prompt: 'consent'}); } else { tokenClient.requestAccessToken({prompt: ''}); } } function createPicker() { const view = new google.picker.View(google.picker.ViewId.DOCS); view.setMimeTypes("application/pdf,image/png,image/jpeg,image/jpg,image/gif,image/svg+xml,application/vnd.ms-excel,application/vnd.google-apps.spreadsheet,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/msword,application/vnd.google-apps.document,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,video/x-flv,video/mp4,video/quicktime,video/mpeg,video/x-matroska,video/x-ms-asf,video/x-ms-wmv,video/avi,audio/mpeg,audio/wav,audio/ac3,audio/aac,audio/ogg,audio/oga,audio/x-m4a,application/zip") const picker = new google.picker.PickerBuilder() .enableFeature(google.picker.Feature.NAV_HIDDEN) .enableFeature(google.picker.Feature.MULTISELECT_ENABLED) .setDeveloperKey(API_KEY) .setAppId(APP_ID) .setOAuthToken(accessToken) .addView(view) .addView(new google.picker.DocsUploadView()) .setCallback(pickerCallback) .toUri() const myDiv = document.getElementById('pickerDiv'); const iframe = document.createElement('iframe'); iframe.setAttribute("src", picker); iframe.style.width = "100%"; iframe.style.height = "100%"; myDiv.appendChild(iframe) } function pickerCallback(data) { if (data.action === google.picker.Action.PICKED) { document.getElementById('content').innerText = JSON.stringify(data, null, 2); } } </script> <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script> <script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script> <style> .picker-dialog{ border: 1px solid red; } </style> </body> </html>
同时附上console.log(picker)的截图内容(已翻译为中文):截图显示生成的Picker URI包含OAuth令牌、应用ID、视图配置等参数,格式为https://docs.google.com/picker?authuser=0&token=xxxx&...。
解决方案
- 放弃手动iframe嵌入,使用官方渲染方法
Google Picker的.toUri()生成的链接依赖页面上下文,直接放入iframe会触发跨域权限限制。修改createPicker函数,用官方API将Picker渲染到自定义Div:
function createPicker() { const view = new google.picker.View(google.picker.ViewId.DOCS); view.setMimeTypes("application/pdf,image/png,image/jpeg,image/jpg,image/gif,image/svg+xml,application/vnd.ms-excel,application/vnd.google-apps.spreadsheet,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/msword,application/vnd.google-apps.document,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,video/x-flv,video/mp4,video/quicktime,video/mpeg,video/x-matroska,video/x-ms-asf,video/x-ms-wmv,video/avi,audio/mpeg,audio/wav,audio/ac3,audio/aac,audio/ogg,audio/oga,audio/x-m4a,application/zip") const picker = new google.picker.PickerBuilder() .enableFeature(google.picker.Feature.NAV_HIDDEN) .enableFeature(google.picker.Feature.MULTISELECT_ENABLED) .setDeveloperKey(API_KEY) .setAppId(APP_ID) .setOAuthToken(accessToken) .addView(view) .addView(new google.picker.DocsUploadView()) .setCallback(pickerCallback) .setOrigin(window.location.origin) // 必须匹配当前页面域名 .build(); // 将Picker内容添加到自定义Div const pickerDiv = document.getElementById('pickerDiv'); pickerDiv.appendChild(picker.getContent()); picker.setVisible(true); }
- 检查云控制台配置
- 确保API密钥已启用Drive Picker API,且当前页面域名在API密钥的“应用限制”列表中。
- 客户端ID的“已授权JavaScript来源”需包含当前页面的完整地址(协议+域名+端口)。
- 理解Picker权限机制
Google Picker的权限验证依赖页面的OAuth上下文,手动构建iframe会破坏这一流程,官方仅推荐使用PickerBuilder提供的方法进行内嵌渲染。
内容的提问来源于stack exchange,提问作者Laxmi Chauhan
相关产品推荐
相关产品推荐

