使用Google Photos API上传图片/视频至Google Photos失败求助
解决Next.js上传文件到Google Photos的问题
核心问题分析
你遇到的问题主要集中在二进制文件的处理方式错误、请求头参数不准确以及状态更新逻辑时序错误上,另外共享相册的权限也可能是mov文件成功返回但不显示的原因。
修正后的完整代码方案
1. 替换文件读取逻辑(废弃FileReader.readAsBinaryString)
readAsBinaryString会将二进制文件转为UTF-8字符串,导致非文本文件(图片、视频)的原始数据损坏,直接使用arrayBuffer获取原始二进制数据:
const onDrop = useCallback(async (acceptedFiles: File[]) => { const file = acceptedFiles[0]; const arrayBuffer = await file.arrayBuffer(); onUpdate({ bytes: arrayBuffer, fileName: file.name, mimeType: file.type, }); }, []);
2. 修正上传令牌请求的参数
确保请求头的文件大小是文件实际字节数,并且直接传递ArrayBuffer作为请求体:
export async function uploadMediaItemGoogle( accessToken: string, uploadMediaItem: UploadMediaItem ) { const url = "https://photoslibrary.googleapis.com/v1/uploads"; const response = await fetch(url, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/octet-stream", "X-Goog-Upload-File-Name": uploadMediaItem.fileName, "X-Goog-Upload-Protocol": "raw", "X-Goog-Upload-Content-Type": uploadMediaItem.mimeType, // 使用ArrayBuffer的byteLength获取真实字节数 "X-Goog-Upload-Raw-Size": uploadMediaItem.bytes.byteLength.toString(), }, body: uploadMediaItem.bytes, method: "POST", }); if (!response.ok) { throw new Error(`上传令牌获取失败: ${response.statusText}`); } const uploadToken = await response.text(); if (!uploadToken) { throw new Error("未获取到上传令牌"); } return createMediaItem(accessToken, uploadMediaItem, uploadToken); }
3. 优化媒体项创建逻辑
增加错误捕获,确保参数传递正确:
export async function createMediaItem( accessToken: string, uploadMediaItem: UploadMediaItem, uploadToken: string ) { const url = "https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate"; const response = await fetch(url, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ albumId: process.env.NEXT_PUBLIC_SHARED_ALBUM_ID!, newMediaItems: [ { description: uploadMediaItem.description || "", simpleMediaItem: { fileName: uploadMediaItem.fileName, uploadToken, }, }, ], }), method: "POST", }); const mediaItemResponse = await response.json(); if (!response.ok) { throw new Error(mediaItemResponse.error?.message || "媒体项创建失败"); } return mediaItemResponse; }
4. 修正调用逻辑的状态更新时序
异步操作完成后再更新加载状态,避免状态错误:
const [uploadMediaItem, setUploadMediaItem] = useState<UploadMediaItem>({ description: "", fileName: "", message: "", mimeType: "", bytes: null, }); async function callApi(token: string) { try { const uploadRes = await uploadMediaItemGoogle(token, uploadMediaItem); console.log({ uploadRes }); } catch (error) { console.error(error); } finally { setUploadingMediaItem(false); setUploadProgress(null); } } function onUploadMediaItem() { if (accessToken && !loadingAccessToken) { setUploadingMediaItem(true); callApi(accessToken); } }
额外关键检查项
- 权限验证:确保OAuth 2.0授权包含
https://www.googleapis.com/auth/photoslibrary.appendonly权限(添加媒体项的最低权限)。 - 共享相册权限:确认目标共享相册设置为允许编辑,只读权限的相册即使上传成功也不会显示媒体项。
- 文件格式:验证上传的png/jpg是标准格式,无损坏;Google Photos支持的格式可参考官方规范。
- 上传令牌有效期:上传令牌仅1小时有效,不可重复使用。
内容的提问来源于stack exchange,提问作者mkteagle
相关产品推荐
相关产品推荐

