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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 23:22:04