【问题标题】:Using Java to do *resumable uploads* using a *signed url* on google cloud storage使用 Java 在谷歌云存储上使用 *签名 url* 进行 *可恢复上传*
【发布时间】:2019-01-15 02:06:05
【问题描述】:

根据有关如何在 google-cloud-storage 中创建对象的文档(请参阅 https://googleapis.github.io/google-cloud-java/google-cloud-clients/apidocs/index.html 中的“create”方法),我们应该在尝试上传大文件时使用 blob.writer(...) 方法文件,因为它可能会以某种方式自动处理可恢复的上传。是这样吗?

但是,如果我们希望在 SIGNED url 上进行可恢复上传,那么在 Java 中如何做到这一点? (非常感谢任何示例代码或指针;到目前为止,我的研究使我相信使用精心创建的 java 库是不可能的,而是需要使用“PUT”和“自定义构建自己的逻辑”在生成签名的 url 后,在 Java 中发布“POST”语句。这是迄今为止“最好”的方式吗?)

【问题讨论】:

    标签: google-api google-cloud-platform google-cloud-storage


    【解决方案1】:

    关于您的第一点,是的,blob.writer(...) 方法确实会自动处理可恢复的上传。不幸的是,这个方法不能从签名的 URL 调用,并且只能直接从字节流上传文件。

    但是,正如您所提到的,可以使用其他方法从签名 URL 创建可恢复上传,例如使用 PUT 方法似乎是一个不错的解决方法。

    我做了以下事情:

    1. 使用“PUT”方法创建signed URL。您可以通过指定SignUrlOption 来做到这一点,我在存储桶中指定了具有所需权限的服务帐户。

    2. 使用 URLFetch 向此签名 URL 抛出 HTTP 请求。例如,我相信您不能直接使用 curl 命令,而 UrlFetch API 可以解决问题。

    3. 将uploadType=resumable 标头添加到urlFetch HTTP 请求。请参阅this documentation 了解其工作原理以及额外的参数和信息。

    4. 我将URLFetch配置为对签名的URL进行异步调用,因为我认为上传大文件时更方便。

    在 App Engine 处理程序中使用的示例代码:

    package com.example.storage;
    
    import java.io.IOException;
    import java.io.FileInputStream;
    import java.util.Properties;
    import java.util.concurrent.TimeUnit;
    import java.util.HashMap;
    import java.util.Map;
    import java.nio.charset.StandardCharsets;
    
    import java.net.URL;
    
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    // Cloud Storage Imports
    import com.google.cloud.storage.Bucket;
    import com.google.cloud.storage.BucketInfo;
    import com.google.cloud.storage.Storage;
    import com.google.cloud.storage.StorageOptions;
    import com.google.cloud.storage.Blob;
    import com.google.cloud.storage.BlobId;
    import com.google.cloud.storage.BlobInfo;
    import com.google.cloud.storage.Storage.SignUrlOption;
    import com.google.auth.oauth2.ServiceAccountCredentials;
    import com.google.cloud.storage.HttpMethod;
    
    // Url Fetch imports
    import com.google.appengine.api.urlfetch.HTTPMethod;
    import com.google.appengine.api.urlfetch.HTTPRequest;
    import com.google.appengine.api.urlfetch.URLFetchService;
    import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
    import com.google.appengine.api.urlfetch.HTTPHeader;
    
    @WebServlet(name = "MainStorage", value = "/")
    public class MainStorage extends HttpServlet {
    
            @Override
            public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
                    // Bucket parameters 
                    String bucketName = "MY-BUCKET-NAME";
                    String blobName = "MY-BLOB-NAME";
                    String keyPath = "/PATH-TO-SERVICE-ACCOUNT-KEY/key.json";
    
                    BlobId blobId = BlobId.of(bucketName, blobName);
                    Storage storage = StorageOptions.getDefaultInstance().getService();
    
                    // Create signed URL with SignUrlOptions
                    URL signedUrl = storage.signUrl(BlobInfo.newBuilder(bucketName, blobName).build(), 14, TimeUnit.DAYS,
                                                    SignUrlOption.signWith(ServiceAccountCredentials.fromStream(new FileInputStream(keyPath))),
                                                    SignUrlOption.httpMethod(HttpMethod.PUT));
    
                    // Contents to upload to the Blob
                    String content = "My-File-contents";
    
                    // Build UrlFetch request
                    HTTPRequest upload_request = new HTTPRequest(signedUrl, HTTPMethod.PUT);
                    upload_request.setPayload(content.getBytes(StandardCharsets.UTF_8));
    
                    // Set request to have an uploadType=resumable
                    HTTPHeader set_resumable = new HTTPHeader("uploadType", "resumable");
                    upload_request.setHeader(set_resumable);
                    URLFetchService fetcher = URLFetchServiceFactory.getURLFetchService();
    
                    // Do an asynchronous call to the signed URL with the contents
                    fetcher.fetchAsync(upload_request);
    
                    // Return response to App Engine handler call
                    response.setContentType("text/plain");
                    response.getWriter().println("Hello Storage");
            }
    }
    

    这可能可以以更好的方式完成,但我相信它提供了有关如何制作此类应用程序的想法。

    【讨论】:

    • 好的,所以如果互联网连接暂时中断,上面的代码将“自动”处理它(由于设置了“可恢复”?)(另外,如果我没有使用 GAE,我想我放弃了“提取器”,而只使用普通的 HttpURLConnection?(如果是这样,HttpUrlConnection 将如何是异步的,因为我在 HttpUrlConnection 的 javadocs 中没有看到任何此类方法)。
    • 不,前面的代码只是创建了可恢复的会话请求。 This documentation 解释了如何检查会话是否被中断,以及如何恢复它(您可以通过向您之前创建的同一会话 URL 发送请求来做到这一点,200-201 响应意味着它已正确结束,308表示不完整)。关于使用什么库而不是 UrlFetch,可以使用任何 REST java 库。我只使用了异步方法,因为 App Engine 中的处理程序有超时,没有必要
    • 我在 Java 中找到了处理异步 HTTP 调用的 this library,可能还有更多示例
    • 如果你想用signedUrl进行用户卷曲:使用signedUrl.toExternalForm()的字符串
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-30
    • 2019-03-19
    • 1970-01-01
    • 2020-06-28
    • 2019-09-09
    • 1970-01-01
    • 2018-09-18
    相关资源
    最近更新 更多