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

使用toUri()将Google Drive Picker嵌入自定义Div的iframe时遇403错误

Google Drive Picker嵌入自定义Div遭遇403权限错误

我正在开发Google Drive Picker,已基于谷歌官方示例代码创建选择器。需要将其嵌入自定义Div/模块中,因此使用Google API的.toUri()方法生成src链接,添加到iframe后嵌入自定义Div。虽然成功嵌入,但出现403错误:

  1. 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&...。


解决方案

  1. 放弃手动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);
}
  1. 检查云控制台配置
  • 确保API密钥已启用Drive Picker API,且当前页面域名在API密钥的“应用限制”列表中。
  • 客户端ID的“已授权JavaScript来源”需包含当前页面的完整地址(协议+域名+端口)。
  1. 理解Picker权限机制
    Google Picker的权限验证依赖页面的OAuth上下文,手动构建iframe会破坏这一流程,官方仅推荐使用PickerBuilder提供的方法进行内嵌渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 13:06:29