Next.js集成Google Photos Picker出现403错误,如何修复?
修复Google Photos选择器403错误(Next.js集成)
问题背景
在Next.js应用中集成Google Photos Library API,尝试实现类似Google Drive选择器的Google Photos选择器时,打开选择器出现403错误。当前代码可生成access token,且通过Axios调用API能正常获取数据,但选择器功能无法正常工作。
原代码
const GooglePhotosPicker = ({ onPick }) => { useEffect(() => { const loadGisClient = () => { window.onGoogleLibraryLoad = () => { const client = google.accounts.oauth2.initTokenClient({ client_id: process.env.CLIENT_ID, scope: "https://www.googleapis.com/auth/photoslibrary.readonly", callback: (tokenResponse) => { if (tokenResponse.error) { console.error( "Error obtaining access token:", tokenResponse.error ); return; } initializePicker(tokenResponse.access_token); }, }); document.getElementById("authorize_button").onclick = () => { client.requestAccessToken(); }; }; const script = document.createElement("script"); script.src = "https://apis.google.com/js/api.js"; script.onload = () => { gapi.load("picker", { callback: onPickerApiLoad }); window.onGoogleLibraryLoad(); }; document.body.appendChild(script); }; loadGisClient(); }, []); const onPickerApiLoad = () => { console.log("Picker API loaded"); }; const initializePicker = (accessToken) => { const picker = new google.picker.PickerBuilder() .addView(google.picker.ViewId.PHOTOS) .setOAuthToken(accessToken) .setDeveloperKey(process.env.KEY) .setCallback(pickerCallback) .build(); picker.setVisible(true); }; const pickerCallback = (data) => { if (data.action === google.picker.Action.PICKED) { const file = data.docs[0]; onPick(file); } }; return ( <div> <button id="authorize_button">Pick a Photo from Google Photos</button> </div> ); }; export default function Page() { const handlePickedPhoto = (photo) => { console.log("Selected photo:", photo); }; return ( <div> <script src="https://accounts.google.com/gsi/client" async defer></script> <GooglePhotosPicker onPick={handlePickedPhoto} /> </div> ); }
修复步骤与关键要点
1. 启用Google Picker API
Google Picker API与Google Photos Library API是独立服务,需在Google Cloud控制台单独启用:
- 进入Cloud控制台的API库,搜索并启用Google Picker API
2. 修正API密钥配置
- 确保
process.env.KEY对应的API密钥,已在Cloud控制台的密钥设置中,将Google Picker API添加到允许调用的API列表 - 检查密钥的应用限制(HTTP referrers),添加你的Next.js应用域名,本地开发需包含
http://localhost:3000等测试地址
3. 调整Next.js环境变量前缀
客户端代码无法读取未加NEXT_PUBLIC_前缀的环境变量,需修改:
- 将
process.env.CLIENT_ID改为process.env.NEXT_PUBLIC_CLIENT_ID - 将
process.env.KEY改为process.env.NEXT_PUBLIC_GOOGLE_API_KEY - 在
.env.local文件中配置对应变量:NEXT_PUBLIC_CLIENT_ID=你的客户端ID NEXT_PUBLIC_GOOGLE_API_KEY=你的API密钥
4. 优化脚本加载与依赖等待
确保Picker API加载完成后再初始化选择器,避免依赖未就绪导致的错误,同时统一脚本加载逻辑:
修复后的代码
const GooglePhotosPicker = ({ onPick }) => { useEffect(() => { let pickerLoaded = false; // 加载Picker API const loadPickerApi = () => { gapi.load('picker', { callback: () => { pickerLoaded = true; console.log("Picker API loaded"); } }); }; // 加载GIS客户端 const loadGisClient = () => { window.onGoogleLibraryLoad = () => { const client = google.accounts.oauth2.initTokenClient({ client_id: process.env.NEXT_PUBLIC_CLIENT_ID, scope: "https://www.googleapis.com/auth/photoslibrary.readonly", callback: (tokenResponse) => { if (tokenResponse.error) { console.error("Error obtaining access token:", tokenResponse.error); return; } // 等待Picker API加载完成后初始化 if (pickerLoaded) { initializePicker(tokenResponse.access_token); } else { setTimeout(() => initializePicker(tokenResponse.access_token), 500); } }, }); document.getElementById("authorize_button").onclick = () => { client.requestAccessToken(); }; }; const apiScript = document.createElement("script"); apiScript.src = "https://apis.google.com/js/api.js"; apiScript.onload = () => { loadPickerApi(); window.onGoogleLibraryLoad(); }; document.body.appendChild(apiScript); }; // 加载GSI认证脚本 const loadGsiScript = () => { const gsiScript = document.createElement("script"); gsiScript.src = "https://accounts.google.com/gsi/client"; gsiScript.async = true; gsiScript.defer = true; document.body.appendChild(gsiScript); }; loadGsiScript(); loadGisClient(); }, []); const initializePicker = (accessToken) => { const picker = new google.picker.PickerBuilder() .addView(google.picker.ViewId.PHOTOS) .setOAuthToken(accessToken) .setDeveloperKey(process.env.NEXT_PUBLIC_GOOGLE_API_KEY) .setCallback(pickerCallback) .build(); picker.setVisible(true); }; const pickerCallback = (data) => { if (data.action === google.picker.Action.PICKED) { const file = data.docs[0]; onPick(file); } }; return ( <div> <button id="authorize_button">Pick a Photo from Google Photos</button> </div> ); }; export default function Page() { const handlePickedPhoto = (photo) => { console.log("Selected photo:", photo); }; return ( <div> <GooglePhotosPicker onPick={handlePickedPhoto} /> </div> ); }
5. 验证OAuth consent screen配置
- 若为外部用户测试,需将应用发布到「生产」状态,或添加测试用户到授权列表
- 确保
https://www.googleapis.com/auth/photoslibrary.readonly已添加到OAuth consent screen的已授权scopes中
内容的提问来源于stack exchange,提问作者Arnob10011
相关产品推荐
相关产品推荐

