0

我正在尝试创建一个签名的 URL 并使用它从我的 PC 将文件上传到谷歌云存储。

我使用 Advanced REST Client(ARC) 作为客户端应用程序。在服务器端,我有一个在 Appengine 上运行的基于球衣的服务器。

我首先从 ARC 发送一个 GET 请求,在接收到该请求后,应用程序引擎会生成一个签名 URL 并将其返回到响应中。

之后,我使用要在正文中上传的文件执行 PUT 请求,并将请求 URL 设置为在对 GET 的响应中收到的内容。

创建签名 URL 的代码片段:

        String encodedUrl = null;
        String contentMD5 = "";
        String contentType = "";
        String httpVerb;


        httpVerb = "PUT";

        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE, 10);
        long expiration = calendar.getTimeInMillis() / 1000L;

        String canonicalizedResource = "/" + bucketName + "/" + objectName;
        String baseURL = "https://storage.googleapis.com" + canonicalizedResource;

        String stringToSign =
                httpVerb + "\n" + contentMD5 + "\n" + contentType + "\n" + expiration + "\n"
                        + canonicalizedResource;

        AppIdentityService service = AppIdentityServiceFactory.getAppIdentityService();
        String googleAccessId = service.getServiceAccountName();

        SigningResult signingResult = service.signForApp(stringToSign.getBytes());
        String encodedSignature = null;
        try {
            encodedSignature =
                    new String(Base64.encodeBase64(signingResult.getSignature(), false), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new InternalServerErrorException();
        }

        String signature = null;
        try {
            signature = URLEncoder.encode(encodedSignature, "UTF-8").toString();
        } catch (UnsupportedEncodingException e) {
            throw new InternalServerErrorException();
        }

        encodedUrl =
                baseURL + "?GoogleAccessId=" + googleAccessId + "&Expires=" + expiration
                        + "&Signature=" + signature;
        System.out.println("Signed URL is: "+encodedUrl);

但是我观察到以下问题:

  1. 每当我发送任何文件类型的 PUT 请求时,都会收到以下错误:

    错误 - 403
    代码 - SignatureDoesNotMatch

    消息 - 我们计算的请求签名与您提供的签名不匹配。检查您的 Google 密钥和签名方法

请注意,在我的代码中,我在创建要签名的字符串时将内容类型设置为“”。此外,在创建 PUT 请求时,我不包含任何 Content-type 标头。

据我了解,如果我在创建签名 URL 时不将 contentType 包含在 stringToSign 中,并且在发送 PUT 请求时也不将其添加为标头,那应该没问题。那么错误的原因可能是什么?

  1. 之后,我通过代码更改并在代码中创建 stringToSign 时添加了 contentType,并在发送 PUT 请求时提供了相应的 Content-Type 标头。

在这种情况下,我可以上传文件,但是上传的文件已修改/损坏。我尝试使用 text/plain 和 image/jpeg。

问题是在文件开头添加了以下文本:

------WebKitFormBoundaryZX8rPPhnm1WXPrUf
Content-Disposition: form-data; name="fileUpload5"; filename="blob"
Content-Type: text/plain

我可以在文本文件中看到这一点,并在十六进制编辑器中打开 .jpg 文件。.jpg 无法在标准图像应用程序中打开,因为该文件已被开头的文本损坏

我在这里错过了什么吗?这是高级 REST 客户端中的任何问题吗?实际上,每当我在正文中发送带有某个文件的 PUT 请求时,我都会在 ARC 中收到一条消息,说: 发送请求时,内容类型标头最终将更改为 multipart/form-data 但是,我保存了导出的所有从 ARC 向文件发送消息,但我没有找到任何 Content-type 标头设置为 multipart/form-data 的消息。那么为什么会出现这条消息,它实际上是一个问题吗?

4

1 回答 1

2

URL 签名代码很棘手,而且很难调试。幸运的是,Google 的 google-cloud 库有一个signUrl功能可以为您解决这个问题。我强烈建议您使用它而不是自己重写它。这是文档

现在,如果您想自己调试它,检查错误消息非常有用。它将包括服务器检查签名的字符串的完整副本。打印出你的stringToSign变量,看看它有什么不同。这会告诉你出了什么问题。

现在,针对您的具体问题:听起来您正在生成一个可接受的签名 URL,但随后您的客户端正尝试上传到 GCS,就好像它正在执行多部分表单上传一样。您正在查看的文本是 HTTP 多部分请求的一部分,“multipart/form-data”警告也指向该方向。查看您正在使用的应用程序是否具有某种您可能不小心使用的“表单”模式/选项?

于 2017-07-04T06:07:45.327 回答