【问题标题】:Uploading to Google Cloud using a signed URL使用签名 URL 上传到 Google Cloud
【发布时间】:2019-12-24 05:22:51
【问题描述】:

我正在尝试使用以下代码从 Google Cloud 生成下载和上传链接,以查看和上传文件:

public class Test {

public static void main(String[] args) throws IOException {
Storage storage = StorageOptions.newBuilder().setCredentials(
    ServiceAccountCredentials.fromStream(new FileInputStream("C:/cred/Key.json")))
    .build()
    .getService();

String filePath = "file/path/";
File file = new File(filePath);
byte[] bytes = Utilities.fileToByteArray(file);
String mimeType = Utilities.getMimeType(bytes);
BlobId blobId = BlobId.of("bucket", file.getName());
BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType(mimeType).build();
URL urlGet = storage
    .signUrl(BlobInfo.newBuilder("bucket", "object").build(), 1, TimeUnit.HOURS,
        SignUrlOption.httpMethod(HttpMethod.GET));
URL urlPut = storage
    .signUrl(blobInfo, 1, TimeUnit.DAYS, SignUrlOption.httpMethod(HttpMethod.PUT),
        SignUrlOption.withContentType());
System.out.println(urlGet);
System.out.println(urlPut);
  }

}

urlGet 包含下载链接,urlPut 包含上传链接。当我运行程序时,我得到以下输出:

https://storage.googleapis.com/roshanbucket/jasperviewpdf?GoogleAccessId=myservice@deft-idiom-234709.iam.gserviceaccount.com&Expires=1552986620&Signature=OZl6M4uMkigu6JBMYoDumgs8P4EC%2BtcK44zHMPkG2xzq3hFfsrj8YYRRtajI8diz64vdCX54nct3wuEpXNRwcnzCmq4KdD53%2B8gbERNuttm8K6%2BlZDLGF3wng%2BCSMzghbGbLnYaZRiZbvjCG%2B3ObBUg9ZiY0qRlif9nyGFftsGUF9GGHvHP6HWP51DJOAurGytSjf9SA5HKPOw4e%2B%2BP1LltfI7m3WjWhxwnSYz4lVxcR4eksec7ILTi66jnwu1gxXtqp75XTxLp9vQa6RC4dCPGoTridFQcMqm89TVzf58c8krk7apQCR6TWp2tAWuFr2xJ1U5FwFfiBaoSX4A33rw%3D%3D

https://storage.googleapis.com/roshanbucket/pi.jpg?GoogleAccessId=myservice@deft-idiom-234709.iam.gserviceaccount.com&Expires=1553069420&Signature=YHsGTgXIBum9t5M7U%2F9fdibDvzBKttQGh0jxzbYgJkevQbpOh8gRQYOlHdjT86byobDE5TNEGF5VrGFAtI5rhRGxLw0xqcNT%2BYGfvHxAIfAJXy5ormXxWVnVEnwGMafyVLOtdIY4asa0niFu%2B36eaIqtD5UzsjUY%2F18OW%2FwvjfQmhlmsvJ7qSkfD1Oif5Rv6c%2F67z1zT7gz7rB4gTCG6mLALuRrOIwCPO%2BkyzOxP9PhEJkoB7j446v%2BhE%2F0pt0wM2nJ29%2BK3HRUShhccJzzZ%2BZRxgOXeUL44CsnYlssaTThU%2FztyUbsXWXbs2hroTcFxVVtOp7aGeCUs1qjdJkXaEg%3D%3D

当我点击第一个链接(即下载)时,它会毫无问题地呈现存储桶中的文件,但是当我使用第二个链接将文件从我的计算机上传到 Google Cloud 时,使用 HTTP PUT 和 Postman ,它给了我以下错误,Status 403:

<?xml version='1.0' encoding='UTF-8'?>
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature 
 you provided. Check your Google secret key and signing method.</Message>
<StringToSign>PUT

 multipart/form-data; boundary=------------------------- 
 -025804137217704409263172
 1553069420
 /roshanbucket/pi.jpg</StringToSign>
 </Error>

我不知道是什么原因造成的。一些帮助将不胜感激。

【问题讨论】:

    标签: java google-cloud-storage http-put


    【解决方案1】:

    经过一段时间的挣扎,终于设法让它运行起来。原来,首先我需要生成一个签名的 URL,相当于

        gsutil signurl -c 'Content-Type' \
       -m RESUMABLE /path/to/your/json_cert_file.json \
        gs://your_bucket/file.txt
    

    然后使用该签名 URL,发送带有 Content-Typex-goog-resumable:start 标头的空 POST 请求,相当于

        curl -v -X 'POST' \
       -H 'content-type: text/plain' \
       -H 'x-goog-resumable:start'  \
       -d '' '<signedURL>'
    

    成功的 POST 将返回状态 201Location 标头以及您可以使用 HTTP PUT 上传文件的实际位置。

    下面是我为完成这个而编写的 Java 类,借助 article

        import com.google.api.client.util.Base64;
        import com.google.auth.oauth2.ServiceAccountCredentials;
        import java.io.DataOutputStream;
        import java.io.File;
        import java.io.FileInputStream;
        import java.io.IOException;
        import java.io.InputStream;
        import java.net.HttpURLConnection;
        import java.net.URL;
        import java.net.URLEncoder;
        import javax.ws.rs.client.Client;
        import javax.ws.rs.client.ClientBuilder;
        import javax.ws.rs.client.Entity;
        import javax.ws.rs.client.ResponseProcessingException;
        import javax.ws.rs.core.Response;
        import uploader.Utilities;
    
        public class Uploader {
    
          private ServiceAccountCredentials creds;    // Service Account Credentials
          private String saEmail;                     // Service Account email
    
          public Uploader() {
            /* Initialize credentials and service account email*/
            try (InputStream inputStream = new FileInputStream("C:/cred/Key.json")) {
              this.creds = ServiceAccountCredentials.fromStream(
                  inputStream);
            } catch (IOException e) {
              e.printStackTrace();
            }
            this.saEmail = "service account email";
          }
    
          /* Sign and return the URL for POST, using credentials from above*/
          private String getSignedUrl(String bucketName, String objectName, String mimeType) {
            String signed_url = null;
            try {
              String verb = "POST";
              long expiration = System.currentTimeMillis() / 1000 + 60;
              String Canonicalized_Extension_Headers = "x-goog-resumable:start";
              String content_type = mimeType;
    
              byte[] sr = creds.sign(
                  (verb + "\n\n" + content_type + "\n" + expiration + "\n" + Canonicalized_Extension_Headers
                      +
                      "\n" + "/" + bucketName + "/" + objectName).getBytes());
              String url_signature = new String(Base64.encodeBase64(sr));
              signed_url = "https://storage.googleapis.com/" + bucketName + "/" + objectName +
                  "?GoogleAccessId=" + saEmail +
                  "&Expires=" + expiration +
                  "&Signature=" + URLEncoder.encode(url_signature, "UTF-8");
            } catch (Exception ex) {
              ex.printStackTrace();
            }
            return signed_url;
          }
    
    
          /* Send POST request to the signed URL using custom headers and an empty body, which returns the actual upload location */
          public String getLocation(String bucketName, String objectName, String mimeType)
              throws IOException {
            URL myURL = new URL(getSignedUrl(bucketName, objectName, mimeType));
            HttpURLConnection myURLConnection = (HttpURLConnection) myURL.openConnection();
            myURLConnection.setRequestMethod("POST");
            myURLConnection.setRequestProperty("Content-Type", mimeType);
            myURLConnection.setRequestProperty("x-goog-resumable", "start");
            // Send post request
            myURLConnection.setDoOutput(true);
            DataOutputStream wr = new DataOutputStream(myURLConnection.getOutputStream());
            wr.flush();
            wr.close();
            int responseCode = myURLConnection.getResponseCode();
            if (responseCode != 201) {
              System.out.println("Request Failed");
            }
            return myURLConnection.getHeaderField("Location");            
          }
    
          /* Do the actual upload and return the PUT Response*/
         public Response doUpload(String url, InputStream inputStream, String mimeType) {
            Response response = null;
            Client client = ClientBuilder.newClient();
            try {
              response = client.target(url)
                  .request()
                  .put(Entity.entity(inputStream, mimeType));
              if (response.getStatus() != 200) {
                System.out.println("Request failed with " + response.getStatus());
              }
            } catch (ResponseProcessingException e) {
              e.printStackTrace();
            }
            return response;
          }   
    
        }
    

    现在,只需在 main 方法中调用它

    public static void main(String[] args) throws Exception {
    Uploader uploader = new Uploader();    
    String filePath = "file/path";
    File file = new File(filePath);
    byte[] bytes = Utilities.fileToByteArray(file); // convert file to bytes
    String mimeType = Utilities.getMimeType(bytes); // bytes from above used with tika
    String url = uploader.getLocation("bucket", file.getName(), mimeType);
    Response r = uploader.doUpload(url, new FileInputStream(file), mimeType);
    System.out.println("Response : " + r.getStatus());
    System.out.println(r.getHeaders());
    }
    

    希望这对某人有所帮助!此方法不需要在Authorization Bearer 中发送带有JwtPOST 请求。

    【讨论】:

      【解决方案2】:

      如果您使用的是 SpringBoot 和 RestTemplate,那么这应该可以工作:

        // IMPORTS
      
        import org.springframework.web.client.RestTemplate;
      
        import org.springframework.core.io.FileSystemResource;
        import org.springframework.http.HttpEntity;
        import org.springframework.http.HttpHeaders;
        import org.springframework.http.HttpMethod;
      
        import java.io.File;
      
      ...
      
        // USE THIS TO UPLOAD YOUR FILE
      
        String signedurl = "https://storage.googleapis.com/..."; // Your signed URL
        File file = new File(); // Your file
        FileSystemResource resource = new FileSystemResource(file);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "plain/text"); // Should be the same MediaType used to create the signedUrl
        HttpEntity<FileSystemResource> requestEntity = new HttpEntity<>(resource, headers);
      
        restTemplate.exchange(signedUrl, HttpMethod.PUT, requestEntity, String.class); // PUT is the HTTP method used to create the signedUrl
      
      ...
      

      【讨论】:

        猜你喜欢
        • 2017-09-16
        • 1970-01-01
        • 1970-01-01
        • 2020-10-31
        • 2016-07-28
        • 2013-01-12
        • 2019-04-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多