使用 Play Games Services Publishing API 上传图片

您可以使用 Play Games Services Publishing API 为游戏资源上传图片。

上传选项

借助 Play Games Services Publishing API,您可以上传特定类型的二进制数据或媒体。对于任何支持上传媒体内容的方法,参考页面上都指定了可以上传的数据的具体特征:

  • 上传文件的大小上限:使用相应方法可以存储的数据量上限。

  • 接受的媒体 MIME 类型:使用相应方法可以存储的二进制数据类型。

您可通过以下任一方法发出上传请求。使用 uploadType 请求参数指定您要使用的方法。

  • 简单上传uploadType=media。适用于快速传输较小的文件,例如 5 MB 或更小的文件。

  • 多部分上传uploadType=multipart。适用于快速传输较小的文件和元数据;需在同一个请求中传输相应文件以及对该文件进行说明的元数据。

  • 可续传上传uploadType=resumable。用于进行可靠的传输;该方法对于传输较大的文件尤为重要。采用这种方法时,您需要发出一个用于启动会话的请求,并可以选择在其中包含元数据。对于绝大多数应用来说,这是一个很好的策略,因为该方法也适用于传输较小的文件,只不过每次上传时都要多发出一个 HTTP 请求。

上传媒体内容时,您需要使用一个特殊的 URI。事实上,支持上传媒体内容的方法有两个 URI 端点:

  • /upload URI,用于媒体内容。上传端点的格式为带有“/upload”前缀的标准资源 URI。您可以在传输媒体数据本身时使用此 URI。

    示例:POST /upload/games/v1configuration/images/resourceId/imageType/imageType

  • 标准资源 URI,用于元数据。如果资源中包含任何数据字段,那么这些字段将会用于存储对上传的文件进行说明的元数据。您可以在创建或更新元数据值时使用此 URI。

    示例:POST /games/v1configuration/images/resourceId/imageType/imageType

简单上传

上传文件最简单的方法就是发出简单上传请求。如果出现以下任一情况,此选项会是理想之选:

  • 文件非常小,就算连接失败需要再次完整上传也没关系。

  • 没有元数据要发送。如果打算在单独的请求中发送相应资源的元数据,或者元数据不受支持或不存在,就可能会出现这种情况。如要使用简单上传,请向该方法的 /upload URI 发出 POST 或 PUT 请求,并在请求中添加查询参数 uploadType=media。例如:

POST https://www.googleapis.com/upload/games/v1configuration/images/resourceId/imageType/imageType?uploadType=media

发出简单上传请求时要使用的 HTTP 标头包括:

  • Content-Type。请将此项设为该方法接受的某种上传媒体数据类型(具体请参阅 Publishing API 参考文档)。

  • Content-Length。请将此项设为您要上传的字节数。如果您采用的是分块传输编码,则不需要设置此项。

示例:简单上传

以下示例展示了如何使用适用于 Play Games Services Publishing API 的简单上传请求。

POST /upload/games/v1configuration/images/resourceId/imageType/imageType?uploadType=media HTTP/1.1
Host: www.googleapis.com
Content-Type: image/png
Content-Length: number_of_bytes_in_file
Authorization: Bearer your_auth_token

PNG data

如果请求成功,服务器将返回 HTTP 200 OK 状态代码以及相应的元数据。例如:

HTTP/1.1 200
Content-Type: application/json

{
  "kind": "gamesConfiguration#imageConfiguration",
  "url": string,
  "resourceId": string,
  "imageType": string
}

多部分上传

如果您有要随上传的数据一起发送的元数据,则可以发出单个 multipart/related 请求。如果您要发送的数据非常小,就算连接失败需要再次完整上传也没关系,那么该方法便是您的理想之选。

如要采用多部分上传模式,请向该方法的 /upload URI 发出 POSTPUT 请求,并在请求中添加查询参数 uploadType=multipart。例如:

POST https://www.googleapis.com/upload/games/v1configuration/images/resourceId/imageType/imageType?uploadType=multipart

发出多部分上传请求时要使用的顶级 HTTP 标头包括:

Content-Type。请将此项设为 multipart/related,并在其中添加您要用于标识请求各个部分的定界字符串。

-Content-Length。请将此项设为请求正文包含的字节总数。请求上传的媒体内容大小必须小于针对该方法规定的文件大小上限。

请求的正文将采用与多部分/相关内容类型 RFC2387 相同的格式,且恰好包含两个部分。这两个部分由定界字符串标识,最后的定界字符串后跟两个连字符。

分段请求的每个部分都需要附加一个 Content-Type 标头:

  • 元数据部分:必须位于第一部分,且 Content-Type 必须与该方法接受的某一元数据格式匹配。

  • 媒体部分:必须位于第二部分,且 Content-Type 必须与该方法接受的某一媒体 MIME 类型匹配。

请参阅 Publishing API 参考文档,了解每个方法接受的媒体 MIME 类型的列表以及上传文件的大小限制。

示例:多部分上传

以下示例显示了针对 Play Games Services Publishing API 的多部分上传请求。

POST /upload/games/v1configuration/images/resourceId/imageType/imageType?uploadType=multipart HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer your_auth_token
Content-Type: multipart/related; boundary=foo_bar_baz
Content-Length: number_of_bytes_in_entire_request_body

--foo_bar_baz
Content-Type: application/json; charset=UTF-8

{
  "kind": "gamesConfiguration#imageConfiguration",
  "url": string,
  "resourceId": string,
  "imageType": string
}

--foo_bar_baz
Content-Type: image/png

PNG data
--foo_bar_baz--

如果请求成功,服务器将返回 HTTP 200 OK 状态代码以及相应的元数据:

HTTP/1.1 200
Content-Type: application/json

{
  "kind": "gamesConfiguration#imageConfiguration",
  "url": string,
  "resourceId": string,
  "imageType": string
}

可续传上传

如需更可靠地上传数据文件,您可以使用“可续传上传”协议。利用该协议,您可以在因通信故障导致数据流中断之后恢复上传操作。如果您要传输大型文件,并且发生网络中断或某些其他传输故障的几率较高(例如,通过移动客户端应用上传时),您会发现该协议特别实用。此外,它还可以在发生网络故障时减少您的带宽使用量,因为您不必从头开始重新上传大型文件。

如需使用可续传上传,请按以下步骤操作:

  1. 启动可续传会话。向包含元数据(如有)的上传 URI 发出初始请求。

  2. 保存可续传会话 URI。保存初始请求的响应中返回的会话 URI;您将会在此会话的后续请求中用到此 URI。上传文件。

  3. 将媒体文件发送到可续传会话 URI。

此外,使用可续传上传的应用需要通过代码来恢复中断的上传。如果上传中断,请查看已成功接收了多少数据,然后从中断处恢复上传。

启动可续传会话

如需启动可续传上传,请向该方法的 /upload URI 发出 POSTPUT 请求,并在请求中添加查询参数 uploadType=resumable。例如:

POST https://www.googleapis.com/upload/games/v1configuration/images/resourceId/imageType/imageType?uploadType=resumable

此启动请求的正文不是为空就是仅包含元数据;您将在后续请求中传输要上传的文件的实际内容。

请在初始请求中使用以下 HTTP 标头:

  • X-Upload-Content-Type。请将此项设为要在后续请求中传输的上传数据的媒体 MIME 类型。

  • X-Upload-Content-Length。请将此项设为要在后续请求中传输的上传数据的字节数。在发起此请求时,如果内容长度未知,那么您可以忽略此标头。

  • Content-Type(如果提供元数据)。根据元数据的数据类型进行设置。

  • Content-Length。请将此项设为该初始请求的正文中提供的字节数。如果您采用的是分块传输编码,则不需要设置此项。

请参阅 Publishing API 参考文档,了解每个方法接受的媒体 MIME 类型的列表以及上传文件的大小限制。

示例:可续传会话的启动请求

以下示例展示了如何针对 Play Games Services Publishing API 启动可续传会话。

POST /upload/games/v1configuration/images/resourceId/imageType/imageType?uploadType=resumable HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer your_auth_token
Content-Length: 38
Content-Type: application/json; charset=UTF-8
X-Upload-Content-Type: image/png
X-Upload-Content-Length: 2000000

{
  "kind": "gamesConfiguration#imageConfiguration",
  "url": string,
  "resourceId": string,
  "imageType": string
}

下一个部分将介绍如何处理响应。

保存可续传会话 URI

如果会话启动请求成功,API 服务器会返回 200 OK HTTP 状态代码。此外,它还会提供 Location 标头来指定您的可续传会话 URI。以下示例中显示的 Location 标头包含 upload_id 查询参数部分,该部分提供了要用于此会话的唯一上传 ID。

示例:可续传会话的启动响应

下面是对第 1 步中的请求的响应:

HTTP/1.1 200 OK
Location: https://www.googleapis.com/upload/games/v1configuration/images/resourceId/imageType/imageType?uploadType=resumable&upload_id=xa298sd_sdlkj2
Content-Length: 0

在上面的响应示例中,所显示的 Location 标头的值即是您将用作 HTTP 端点以上传实际文件或查询上传状态的会话 URI。

请复制并保存该会话 URI,以便在后续请求中使用。

上传文件

如需上传文件,请向您在上一步中获得的上传 URI 发送 PUT 请求。上传请求的格式如下所示:

PUT session_uri

发出可续传文件上传请求时要使用的 HTTP 标头包含 Content-Length。请将此项设为您要在该请求中上传的字节数(通常为上传文件大小)。

示例:可续传文件上传请求

下面是一个可续传请求,用于针对当前示例上传大小为 2,000,000 字节的完整 PNG 文件。

PUT https://www.googleapis.com/upload/games/v1configuration/images/resourceId/imageType/imageType?uploadType=resumable&upload_id=xa298sd_sdlkj2 HTTP/1.1
Content-Length: 2000000
Content-Type: image/png

bytes 0-1999999

如果请求成功,服务器将会返回 HTTP 201 Created 以及与此资源相关联的所有元数据。如果可续传会话的初始请求为 PUT(更新现有资源),则服务器将会返回成功响应 200 OK 以及与此资源关联的所有元数据。

如果上传请求被中断,或者服务器返回了 HTTP 503 Service Unavailable 或任何其他 5xx 响应,请按照恢复中断的上传部分中列出的步骤进行操作。


分块上传文件

借助可续传上传,您可以将一个文件分成几块,然后发送一系列请求,按顺序上传每个分块。这并不是首选方法,因为发送更多请求会降低性能,而且通常也不需要这样做。不过,您可能会需要使用分块来减少单个请求中传输的数据量。如果单个请求有固定的时间限制(例如某些类别的 Google App Engine 请求),那么该方法会非常实用。对于默认情况下不支持显示上传进度的旧版浏览器,该方法还可以让您提供上传进度指示。

如果您要分块上传数据,那么除了上传完整文件所需的 Content-Length 标头之外,您还必须提供 Content-Range 标头:

  • Content-Length。请将此项设为相应分块的大小;若是针对最后一个请求,可设为更小。

  • Content-Range:请设置此项来指明要上传目标文件中的哪些字节。例如,Content-Range: bytes 0-524287/2000000 表示您要上传某个 2,000,000 字节文件中的前 524,288 个字节 (256 x 1024 x 2)。

示例:可续传分块文件上传请求

下面的示例请求将会发送前 524,288 个字节:

PUT {session_uri} HTTP/1.1
Host: www.googleapis.com
Content-Length: 524288
Content-Type: image/png
Content-Range: bytes 0-524287/2000000

bytes 0-524288

如果请求成功,服务器将会返回下面的 308 Resume Incomplete 和 Range 标头,后者用于标识目前已存储的字节总数:

HTTP/1.1 308 Resume Incomplete
Content-Length: 0
Range: bytes=0-524287

使用 Range 标头中返回的上限值来确定从何处开始上传下一个分块。继续 PUT 文件的每个分块,直到整个文件上传完成。

如有任何分块的 PUT 请求被中断,或者服务器返回了 HTTP 503 Service Unavailable 或任何其他 5xx 响应,请按照恢复中断的上传部分中列出的步骤进行操作,但这不会上传文件的剩余部分,而只是从中断处继续上传各分块。

重要说明

  • 请务必使用响应中的 Range 标头来确定从何处开始上传下一个分块;请勿假定服务器已收到上一个请求中发送的所有字节。

  • 每个上传 URI 的生命周期都是有限的,最终都会过期(若不使用,有效期为 1 天左右)。因此,您最好在获得上传 URI 后立即执行可续传上传操作,并在发生中断后立即恢复中断的上传。

  • 如果您发送的请求包含过期的上传会话 ID,服务器会返回 404 Not Found 状态代码。如果上传会话发生不可恢复的错误,服务器会返回 410 Gone 状态代码。在这些情况下,您必须启动新的可续传上传操作,获得新的上传 URI,然后使用新的端点从头开始上传。

当整个文件上传完毕后,服务器将返回 HTTP 201 Created 以及与此资源关联的所有元数据。如果此请求是为了更新现有实体(而非创建新实体),那么已完成的上传对应的 HTTP 响应代码将为 200 OK


恢复中断的上传

如果上传请求在收到响应之前被终止,或者您收到来自服务器的 HTTP 503 Service Unavailable 响应,那么您将需要恢复中断的上传。如需恢复中断的上传,请执行以下操作:

  1. 请求状态。通过向上传 URI 发出空的 PUT 请求,查询上传的当前状态。对于该请求,HTTP 标头应包含一个 Content-Range 标头,用于表明文件中的当前位置未知。例如,如果您的文件总长度为 2,000,000,请将 Content-Range 设为 */2000000。如果您不知道文件的完整大小,请将 Content-Range 设为 */*

  2. 获取已上传的字节数。解析服务器针对状态查询返回的响应。 服务器会在其响应中使用 Range 标头来指明目前已收到哪些字节。例如,如果 Range 标头为 0-299999,则表示已收到文件的前 300,000 个字节。

  3. 上传剩余的数据。最后,在您知道应从何处恢复请求后,请发送剩余的数据或当前分块。请注意,无论在何种情况下,您都需要将剩余的数据视为单独的分块。因此,在恢复上传时,您需要发送 Content-Range 标头。

示例:恢复中断的上传

  1. 请求了解上传状态。下面的请求使用 Content-Range 标头来指明 2,000,000 字节文件中的当前位置未知。

    PUT {session_uri} HTTP/1.1
    Content-Length: 0
    Content-Range: bytes */2000000
    
  2. 从响应中提取目前已上传的字节数。服务器的响应使用 Range 标头来指明目前已收到文件的前 43 个字节。使用 Range 标头的上限值来确定从何处开始续传。

HTTP/1.1 308 Resume Incomplete
Content-Length: 0
Range: 0-42
  1. 从中断处恢复上传。下面的请求通过发送文件中的剩余字节(从第 43 个字节开始)来恢复上传。
PUT {session_uri} HTTP/1.1
Content-Length: 1999957
Content-Range: bytes 43-1999999/2000000

bytes 43-1999999

最佳实践

为成功上传媒体内容,了解一些与错误处理相关的最佳实践会对您有所帮助。

  • 恢复或重试由于连接中断或任何 5xx 错误而失败的上传,此类错误包括:

    • 500 Internal Server Error
    • 502 Bad Gateway
    • 503 Service Unavailable
    • 504 Gateway Timeout
  • 如果在恢复或重试上传请求时收到任何 5xx 服务器错误,请使用指数退避算法策略。如果服务器发生超载,则可能会出现此类错误。在出现大量请求或网络流量拥堵期间,指数退避算法有助于缓解这类情况。

  • 您不能通过指数退避算法来处理其他类型的请求,但仍然可以重试其中的许多请求。重试这些请求时,请限制重试的次数。例如,您的代码可以限制为重试十次或更少的次数就会报告错误。

  • 执行可续传上传时,通过从头开始整个上传过程来处理 404 Not Found410 Gone 错误。

指数退避算法

指数退避算法是网络应用的标准错误处理策略,在此策略中,客户端按照不断增加的时间间隔定期重试失败的请求。如果因存在大量请求或网络流量拥堵而导致服务器返回错误,指数退避算法策略能够很好地处理此类错误。反过来说,此策略并不适合处理与网络流量或响应时间无关的错误,例如授权凭据无效或未找到文件。

如果使用得当,指数退避算法还能提高带宽使用效率、减少获得成功响应所需的请求次数,并最大程度地提高并发环境中的请求吞吐量。

实现简单指数退避算法的流程如下:

  1. 向 API 发出请求。
  2. 收到 HTTP 503 响应,指出您应重试该请求。
  3. 等待 1 秒 + random_number_milliseconds 毫秒,然后重试该请求。
  4. 收到 HTTP 503 响应,指出您应重试该请求。
  5. 等待 2 秒 + random_number_milliseconds 毫秒,然后重试该请求。
  6. 收到 HTTP 503 响应,指出您应重试该请求。
  7. 等待 4 秒 + random_number_milliseconds 毫秒,然后重试该请求。
  8. 收到 HTTP 503 response,指出您应重试该请求。
  9. 等待 8 秒 + random_number_milliseconds 毫秒,然后重试该请求。
  10. 收到 HTTP 503 response,指出您应重试该请求。
  11. 等待 16 秒 + random_number_milliseconds 毫秒,然后重试该请求。
  12. 停止。报告或记录错误。

在上述列表中,random_number_milliseconds 是一个小于或等于 1000 的随机毫秒数。这是一项很有必要的机制,因为引入一个较小的随机延迟时间有助于更均匀地分布负载,并降低服务器发生拥堵的可能性。random_number_milliseconds 的值必须在每次等待后重新定义。

该算法设置为 n 为 5 时终止。设置此上限的目的在于防止客户端无限制地重试。此上限产生的结果是总延迟时间达到 32 秒左右才会将请求视为“不可恢复的错误”。也可以设置更高的重试次数上限,特别是在上传用时比较长的情况下;只需确保所设的重试延迟时间上限合理即可(比如说少于 1 分钟)。

API 客户端库指南