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

问题描述:

我想创建一个签名的URL,并从我的电脑上传文件到谷歌云存储使用它。无法成功上传文件使用签名的URL到谷歌云存储从高级REST客户端

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

我首先从ARC发送一个GET请求,接收哪个app引擎生成一个签名的URL并将它返回给响应。

之后,我做了一个PUT请求,我要在正文中上传文件,请求URL设置为在GET响应中收到的内容。

的代码片断创建签署网址:

 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时不在stringToSign中包含contentType,并且在发送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的消息。 那么为什么这个消息来了,它实际上是一个问题?

网址签名的代码是棘手和非常难以调试。幸运的是,谷歌的谷歌云库有一个signUrl函数为您处理这件事。我强烈建议你使用它,而不是自己重写它。 Here's the documentation。现在

,如果你想自己调试它,检查错误消息是超级有用。它将包括服务器检查签名的字符串的完整副本。打印出你的stringToSign变量,看看它有什么不同。那会告诉你什么是错的。

现在,您的具体问题:它听起来像你生成一个可以接受已签署的网址,但后来您的客户尝试就好像它是做多,形式上传上传到GCS。您正在查看的文本是HTTP多部分请求的一部分,“multipart/form-data”警告也指出了该方向。看看您使用的应用程序是否有某种您可能意外使用的“表单”模式/选项?

+1

谢谢。正如您所提到的那样,高级REST客户端似乎存在一些问题,因为即使指定了其他内容类型,它也会将上载作为多部分表单数据进行上传。我找不到如何更改设置,但我现在使用curl命令行工具进行上传/下载,并且工作正常。 –