如何在 App Engine 上使用 Google 客户端库为 Java 创建 Google 云存储可恢复上传 URL?

How do I create a Google Cloud Storage resumable upload URL with Google Client Library for Java on App Engine?

我找到了下面的注释,它准确地描述了我想做的事情:

Note: If your users are only uploading resources (writing) to an access-controlled bucket, you can use the resumable uploads functionality of Google Cloud Storage, and avoid signing URLs or requiring a Google account. In a resumable upload scenario, your (server-side) code authenticates and initiates an upload to Google Cloud Storage without actually uploading any data. The initiation request returns an upload ID, which can then be used in a client request to upload the data. The client request does not need to be signed because the upload ID, in effect, acts as an authentication token. If you choose this path, be sure to transmit the upload ID over HTTPS.

https://cloud.google.com/storage/docs/access-control#Signed-URLs

但是,我不知道如何使用 Java 的 Google 云存储库执行此操作。

https://developers.google.com/resources/api-libraries/documentation/storage/v1/java/latest/

我找不到任何对可恢复文件的引用,也找不到此 API 中任何文件的 URL。我该怎么做?

该库不会将其创建的 URL 公开给其调用者,这意味着您不能使用它来完成此操作。如果你想使用带符号的 URLs 或你上面提到的技巧,你需要手动实现它。

如果可能,我建议使用已签名的 URL 解决方案,而不是服务器初始化可恢复上传的解决方案。它更灵活,更容易正确,后一种方法有一些奇怪的边缘情况,您可以 运行 进入。

不久前有人在另一个问题中写了一个从 App Engine 签署 URL 的快速示例:

我花了一些时间进行挖掘,但我想出了以下正确的方法。一些关于如何执行此操作的官方文档会很好,特别是因为实际触发可恢复上传的端点与文档中调用的不同。这里的内容来自于使用 gsutil 工具对请求进行签名,然后计算出正在执行的操作。 under-documented 额外的事情是,POST 到此 URL 以获取可恢复的 session URL 的代码必须包含 "x-goog-resumable: start" header 才能触发上传。从那里开始,一切都与执行可恢复上传到 GCS 的文档相同。

import base64
import datetime
import time
import urllib

from google.appengine.api import app_identity

SIGNED_URL_EXPIRATION = datetime.timedelta(days=7)

def SignResumableUploadUrl(gcs_resource_path):
  """Generates a signed resumable upload URL.

  Note that documentation on this ability is sketchy. The canonical source
  is derived from running the gsutil program to generate a RESUMABLE URL
  with the "-m RESUMABLE" argument. Run "gsutil help signurl" for info and
  the following for an example:
    gsutil -m RESUMABLE -d 10m keyfile gs://bucket/file/name

  Note that this generates a URL different from the standard mechanism for
  deriving a resumable start URL and the initiator needs to add the header:
    x-goog-resumable:start

  Args:
    gcs_resource_path: The path of the GCS resource, including bucket name.

  Returns:
    A full signed URL.
  """
  method = "POST"
  expiration = datetime.datetime.utcnow() + SIGNED_URL_EXPIRATION
  expiration = int(time.mktime(expiration.timetuple()))
  signature_string = "\n".join([
      method,
      "",  # content md5
      "",  # content type
      str(expiration),
      "x-goog-resumable:start",
      gcs_resource_path
  ])
  _, signature_bytes = app_identity.sign_blob(signature_string)
  signature = base64.b64encode(signature_bytes)

  query_params = {
      "GoogleAccessId": app_identity.get_service_account_name(),
      "Expires": str(expiration),
      "Signature": signature,
  }

  return "{endpoint}{resource}?{querystring}".format(
      endpoint="https://storage.googleapis.com",
      resource=gcs_resource_path,
      querystring=urllib.urlencode(query_params))

您可以自己构建 url。这是一个例子:

OkHttpClient client = new OkHttpClient();
AppIdentityService appIdentityService = credential.getAppIdentityService();
Collection<String> scopes = credential.getScopes();
String accessToken = appIdentityService.getAccessToken(scopes).getAccessToken();
Request request = new Request.Builder()
        .url("https://www.googleapis.com/upload/storage/v1/b/" + bucket + "/o?name=" + fileName + "&uploadType=resumable")
        .post(RequestBody.create(MediaType.parse(mimeType), new byte[0]))
        .addHeader("X-Upload-Content-Type", mimeType)
        .addHeader("X-Upload-Content-Length", "" + length)
        .addHeader("Origin", "http://localhost:8080")
        .addHeader("Origin", "*")
        .addHeader("authorization", "Bearer "+accessToken)
        .build();
Response response = client.newCall(request).execute();
return response.header("location");