无法使用已签名的 URL 将文件从高级 REST 客户端成功上传到 google 云存储

Not able to successfully upload files using signed URL to google cloud Storage from Advanced REST Client

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

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

我首先从 ARC 发送一个 GET 请求,应用引擎收到该请求后会生成一个已签名的 URL 并 returns 它返回响应。

之后,我对 body 中要上传的文件执行 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 header.

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

  1. 之后我通过代码更改并在代码中创建 stringToSign 时添加了 contentType 并在发送 PUT 请求时也给出了相应的 Content-Type header。

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

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

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

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

我是不是漏掉了什么?这是高级 REST 客户端中的任何问题吗? 实际上,每当我发送带有 body 中某个文件的 PUT 请求时,我都会在 ARC 中收到一条消息,内容如下: 发送请求时content-typeheader最终会变成multipart/form-data 但是,我保存了从 ARC 导出的所有消息到一个文件,但我没有找到 Content-type header 设置为 multipart/form-data 的任何消息。 那么为什么会出现此消息,它实际上是一个问题吗?

URL-签名代码很棘手,而且众所周知很难调试。幸运的是,Google 的 google-cloud 库有一个 signUrl 函数可以为您处理这个问题。我强烈建议您使用它而不是自己重写它。 Here's the documentation.

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

现在,关于您的具体问题:听起来您正在生成可接受的签名 URL,但是您的客户端正在尝试上传到 GCS,就好像它正在执行多部分表单上传一样。您正在查看的文本是 HTTP 多部分请求的一部分,"multipart/form-data" 警告也指向那个方向。查看您正在使用的应用程序是否包含您可能不小心使用的某种 "Form" mode/option?