上传媒体内容

上传媒体内容的过程分为两个步骤:

  1. 使用上传端点将媒体文件的字节上传到 Google 服务器。 此方法会返回一个上传令牌,用于标识上传的字节。
  2. 结合使用 batchCreate 调用和上传令牌,在用户的 Google 相册账号中创建媒体内容。

以下步骤概述了上传单项媒体内容的过程。如果您要上传多项媒体内容(很可能适用于任何正式版应用),请查看上传最佳做法,以提高上传效率。

准备工作

必需的授权范围

将媒体内容上传到用户的媒体库或影集需要 photoslibrary.appendonlyphotoslibrary 范围。

您也可以使用 photoslibrary.sharing 范围创建媒体内容。如需使用 photoslibrary.sharing 范围创建内容,您必须先创建一个影集,并使用 shareAlbum 将其标记为已分享。然后,您可以在影集中创建与该用户共享的媒体内容。您无法直接在用户的媒体库或应用尚未共享的影集中创建内容。

列出影集时,isWriteable 属性会指明应用是否有权在特定影集中创建媒体内容。

接受的文件类型和大小

您可以上传下表中列出的文件类型。

媒体类型 接受的文件类型 文件大小上限
照片 AVIF、BMP、GIF、HEIC、ICO、JPG、PNG、TIFF、WEBP 和部分 RAW 文件。 200 MB
视频 3GP、3G2、ASF、AVI、DIVX、M2T、M2TS、M4V、MKV、MMV、MOD、MOV、MP4、MPG、MTS、TOD、WMV。 20 GB

第 1 步:上传字节

使用上传请求将字节上传到 Google。成功的上传请求会以原始文本字符串的形式返回上传令牌。使用这些上传令牌可通过 batchCreate 调用创建媒体内容。

REST

在 POST 请求标头中包含以下字段:

标题字段
Content-type 设置为 application/octet-stream
X-Goog-Upload-Content-Type 推荐。请将此项设为您要上传的字节的 MIME 类型。 常见 MIME 类型包括 image/jpegimage/pngimage/gif
X-Goog-Upload-Protocol 设置为 raw

以下是 POST 请求标头:

POST https://photoslibrary.googleapis.com/v1/uploads
Authorization: Bearer oauth2-token
Content-type: application/octet-stream
X-Goog-Upload-Content-Type: mime-type
X-Goog-Upload-Protocol: raw

在请求正文中添加文件的二进制文件:

media-binary-data

如果此 POST 请求成功,则会返回一个原始文本字符串形式的上传令牌作为响应正文。如需创建媒体内容,请在 batchCreate 调用中使用这些文本字符串。

upload-token

Java

// Open the file and automatically close it after upload
try (RandomAccessFile file = new RandomAccessFile(pathToFile, "r")) {
  // Create a new upload request
  UploadMediaItemRequest uploadRequest =
      UploadMediaItemRequest.newBuilder()
              // The media type (e.g. "image/png")
              .setMimeType(mimeType)
              // The file to upload
              .setDataFile(file)
          .build();
  // Upload and capture the response
  UploadMediaItemResponse uploadResponse = photosLibraryClient.uploadMediaItem(uploadRequest);
  if (uploadResponse.getError().isPresent()) {
    // If the upload results in an error, handle it
    Error error = uploadResponse.getError().get();
  } else {
    // If the upload is successful, get the uploadToken
    String uploadToken = uploadResponse.getUploadToken().get();
    // Use this upload token to create a media item
  }
} catch (ApiException e) {
  // Handle error
} catch (IOException e) {
  // Error accessing the local file
}

PHP

try {
    // Create a new upload request by opening the file
    // and specifying the media type (e.g. "image/png")
    $uploadToken = $photosLibraryClient->upload(file_get_contents($localFilePath), null, $mimeType);
} catch (\GuzzleHttp\Exception\GuzzleException $e) {
    // Handle error
}

图片的建议文件大小小于 50 MB。超过 50 MB 的文件很容易出现性能问题。

Google Photos Library API 支持可续传上传。借助可续传上传,您可以将媒体文件拆分为多个部分,然后一次上传一个部分。

第 2 步:创建媒体内容

上传媒体文件的字节后,您可以使用上传令牌在 Google 相册中将它们创建为媒体内容。上传令牌的有效期为创建后的一天。媒体内容始终会添加到用户的媒体库中。媒体内容只能添加到应用创建的影集中。如需了解详情,请参阅授权范围

如需创建新的媒体内容,请通过指定 newMediaItems 列表来调用 mediaItems.batchCreate。每个 newMediaItem 均包含 simpleMediaItem 中指定的上传令牌,以及向用户显示的可选说明。

说明字段不得超过 1000 个字符,且应仅包含用户创建的有意义的文字。例如,“我们的公园之旅”或“节日晚餐”。请勿添加元数据(例如文件名、程序化代码或其他自动生成的文本)。

为获得最佳性能,请在一次调用中添加多项媒体内容,从而减少对 mediaItems.batchCreate 的调用次数。请始终等到上一个请求完成后再为同一用户进行后续调用。

您可以通过指定说明和相应的上传令牌,在用户媒体库中创建单项或多项媒体内容:

REST

以下是 POST 请求标头:

POST https://photoslibrary.googleapis.com/v1/mediaItems:batchCreate
Content-type: application/json
Authorization: Bearer oauth2-token

请求正文应指定 newMediaItems 列表。

{
  "newMediaItems": [
    {
      "description": "item-description",
      "simpleMediaItem": {
        "fileName": "filename",
        "uploadToken": "upload-token"
      }
    }
   , ...
  ]
}

Java

try {
  // Create a NewMediaItem with the following components:
  // - uploadToken obtained from the previous upload request
  // - filename that will be shown to the user in Google Photos
  // - description that will be shown to the user in Google Photos
  NewMediaItem newMediaItem = NewMediaItemFactory
          .createNewMediaItem(uploadToken, fileName, itemDescription);
  List<NewMediaItem> newItems = Arrays.asList(newMediaItem);

  BatchCreateMediaItemsResponse response = photosLibraryClient.batchCreateMediaItems(newItems);
  for (NewMediaItemResult itemsResponse : response.getNewMediaItemResultsList()) {
    Status status = itemsResponse.getStatus();
    if (status.getCode() == Code.OK_VALUE) {
      // The item is successfully created in the user's library
      MediaItem createdItem = itemsResponse.getMediaItem();
    } else {
      // The item could not be created. Check the status and try again
    }
  }
} catch (ApiException e) {
  // Handle error
}

PHP

try {
    $newMediaItems = [];
    // Create a NewMediaItem with the following components:
    // - uploadToken obtained from the previous upload request
    // - filename that will be shown to the user in Google Photos
    // - description that will be shown to the user in Google Photos
    $newMediaItems[0] = PhotosLibraryResourceFactory::newMediaItemWithDescriptionAndFileName(
            $uploadToken, $itemDescription, $fileName);

    $response = $photosLibraryClient->batchCreateMediaItems($newMediaItems);
    foreach ($response->getNewMediaItemResults() as $itemResult) {
        $status = $itemResult->getStatus();
        if ($status->getCode() != Code::OK) {
            // Error while creating the item.
        }
    }
} catch (\Google\ApiCore\ApiException $e) {
    // Handle error
}


您可以通过指定影集 id,将媒体内容添加到媒体库和影集。有关详情,请参阅创建影集

每个影集最多可包含 20,000 项媒体内容。如果在影集中创建超出此限制的媒体内容,则请求将失败。

REST

{
  "albumId": "album-id",
  "newMediaItems": [
    {
      "description": "item-description",
      "simpleMediaItem": {
        "fileName": "filename",
        "uploadToken": "upload-token"
      }
    }
   , ...
  ]
}

Java

try {
  // Create new media items in a specific album
  BatchCreateMediaItemsResponse response = photosLibraryClient
      .batchCreateMediaItems(albumId, newItems);
  // Check the response
} catch (ApiException e) {
  // Handle error
}

PHP

try {
    $response = $photosLibraryClient->batchCreateMediaItems($newMediaItems, ['albumId' => $albumId]);
} catch (\Google\ApiCore\ApiException $e) {
    // Handle error
}

您还可以指定 albumIdalbumPosition,以在影集中的特定位置插入媒体内容。

REST

{
  "albumId": "album-id",
  "newMediaItems": [
    {
      "description": "item-description",
      "simpleMediaItem": {
        "fileName": "filename",
        "uploadToken": "upload-token"
      }
    }
    , ...
  ],
  "albumPosition": {
    "position": "after-media-item",
    "relativeMediaItemId": "media-item-id"
  }
}

Java

try {
  // Create new media items in a specific album, positioned after a media item
  AlbumPosition positionInAlbum = AlbumPositionFactory.createFirstInAlbum();
  BatchCreateMediaItemsResponse response = photosLibraryClient
      .batchCreateMediaItems(albumId, newItems, positionInAlbum);
  // Check the response
} catch (ApiException e) {
  // Handle error
}

PHP

try {
    $albumPosition = PhotosLibraryResourceFactory::albumPositionAfterMediaItem($mediaItemId);
    $response = $photosLibraryClient->batchCreateMediaItems($newMediaItems,
        ['albumId' => $albumId, 'albumPosition' => $albumPosition]);
} catch (\Google\ApiCore\ApiException $e) {
    // Handle error
}

有关在影集中定位的详情,请参阅添加扩充项

内容创建响应

mediaItems.batchCreate 调用会返回您尝试创建的每项媒体内容的结果。newMediaItemResults 列表会指示状态,并包含请求的 uploadToken。状态代码为非零表示错误。

REST

如果所有媒体内容均已成功创建,则请求将返回 HTTP 状态 200 OK。如果无法创建某些媒体内容,请求会返回 HTTP 状态 207 MULTI-STATUS,表示部分内容成功。

{
  "newMediaItemResults": [
    {
      "uploadToken": "upload-token",
      "status": {
        "message": "Success"
      },
      "mediaItem": {
        "id": "media-item-id",
        "description": "item-description",
        "productUrl": "https://photos.google.com/photo/photo-path",
        "mimeType": "mime-type",
        "mediaMetadata": {
          "width": "media-width-in-px",
          "height": "media-height-in-px",
          "creationTime": "creation-time",
          "photo": {}
        },
        "filename": "filename"
      }
    },
    {
      "uploadToken": "upload-token",
      "status": {
        "code": 13,
        "message": "Internal error"
      }
    }
  ]
}

Java

BatchCreateMediaItemsResponse response = photosLibraryClient.batchCreateMediaItems(newItems);

// The response contains a list of NewMediaItemResults
for (NewMediaItemResult result : response.getNewMediaItemResultsList()) {
  // Each result item is identified by its uploadToken
  String uploadToken = result.getUploadToken();
  Status status = result.getStatus();

  if (status.getCode() == Code.OK_VALUE) {
    // If the request is successful, a MediaItem is returned
    MediaItem mediaItem = result.getMediaItem();
    String id = mediaItem.getId();
    String productUrl = mediaItem.getProductUrl();
    // ...
  }
}

PHP

// The response from a call to batchCreateMediaItems returns a list of NewMediaItemResults
foreach ($response->getNewMediaItemResults() as $itemResult) {
    // Each result item is identified by its uploadToken
    $itemUploadToken = $itemResult->getUploadToken();
    // Verify the status of each entry to ensure that the item has been uploaded correctly
    $itemStatus = $itemResult->getStatus();
    if ($itemStatus->getCode() != Code::OK) {
        // Error when item is being created
    } else {
        // Media item is successfully created
        // Get the MediaItem object from the response
        $mediaItem = $itemResult->getMediaItem();
        // It contains details such as the Id of the item, productUrl
        $id = $mediaItem->getId();
        $productUrl = $mediaItem->getProductUrl();
        // ...
    }
}

如果成功添加项,则返回 mediaItem,其中包含其 mediaItemIdproductUrlmediaMetadata。有关详情,请参阅访问媒体内容

如果媒体内容是视频,则必须先对其进行处理。mediaItemmediaMetadata 内包含一个 status,用于描述视频文件的处理状态。新上传的文件会首先返回 PROCESSING 状态,然后才能成为 READY 状态,以供使用。如需了解详情,请参阅访问媒体内容

如果您在此调用期间遇到错误,请遵循最佳做法并重试您的请求。您可能需要跟踪成功添加的内容,以便在下次请求期间将图片插入到影集中的正确位置。如需了解详情,请参阅创建影集

返回结果的顺序始终与上传令牌的提交顺序相同。

有关上传的最佳做法

以下最佳实践和资源有助于提高上传的总体效率:

  • 使用我们支持的客户端库之一。
  • 遵循重试和错误处理最佳实践,并牢记以下几点:
    • 您的配额已用尽时,或者您因过于频繁地发出过多调用而受到速率限制,可能会发生 429 错误。在上一个请求完成之前,请勿针对同一用户调用 batchCreate
    • 429 个错误需要至少 30s的延迟才能重试。重试请求时请使用指数退避算法策略。
    • 如果服务器遇到错误,就会出现 500 错误。上传时,这很可能是由于同时为同一用户进行多次写入调用(例如 batchCreate)所致。请检查请求的详细信息,不要并行调用 batchCreate
  • 使用可续传上传流程,使上传在网络中断时更加稳健,通过恢复部分完成的上传来减少带宽使用量。从客户端移动设备上传或上传大型文件时,这一点非常重要。

此外,针对上传流程的每个步骤,请考虑以下提示:上传字节,然后创建媒体内容

上传字节

创建媒体内容

  • 请勿为单个用户并行调用 batchCreate

    • 对于每个用户,依次(按顺序)调用 batchCreate
    • 对于多用户,请始终为每位用户逐一调用 batchCreate。只能并行为不同的用户发出调用请求。
  • 在每次调用 batchCreate 时添加尽可能多的 NewMediaItems,以最大限度地减少必须进行的调用总数。最多可添加 50 个项目。

  • 设置由用户创建的有意义的说明文字。请勿在说明字段中添加元数据,例如文件名、程序化标记或其他自动生成的文本。

示例演示

此示例使用伪代码来逐步完成为多个用户上传媒体内容的过程。目的是概述上传流程的两个步骤(上传原始字节创建媒体内容),并详细说明构建高效且有弹性的上传集成的最佳实践。

第 1 步:上传原始字节

首先创建一个队列,以上传来自所有用户的媒体内容的原始字节。跟踪每位用户返回的 uploadToken。请注意以下要点:

  • 并发上传线程数取决于您的操作环境。
  • 建议您根据需要对上传队列进行重新排序。例如,您可以根据每位用户的剩余上传数量、用户的总体进度或其他要求来确定上传优先级。

伪代码

CREATE uploadQueue FROM users, filesToUpload
// Upload media bytes in parallel.
START multiple THREADS
  WHILE uploadQueue is not empty
    POP uploadQueue
    UPLOAD file for user
    GET uploadToken
    CHECK and HANDLE errors
    STORE uploadToken for user in uploadTokensQueue
  END

第 2 步:创建媒体内容

在第 1 步中,您可以并行上传来自多个用户的多个字节,但在第 2 步中,您一次只能为每个用户发起一次调用。

伪代码

// For each user, create media items once 50 upload tokens have been
// saved, or no more uploads are left per user.
WHEN uploadTokensQueue for user is >= 50 OR no more pending uploads for user
  // Calls can be made in parallel for different users,
  // but only make a single call per user at a time.
  START new thread for (this) user if there is no thread yet
    POP 50 uploadTokens from uploadTokensQueue for user
    CALL mediaItems.batchCreate with uploadTokens
    WAIT UNTIL batchCreate call has completed
    CHECK and HANDLE errors (retry as needed)
  DONE.

继续此过程,直到所有上传和媒体创建调用都完成。