使用“gzip”编码为“application / octet-stream”类型内容读取云存储内容

时间:2014-01-02 02:00:21

标签: google-app-engine google-cloud-storage

我们正在为应用引擎使用“Google云存储客户端库”,在文件创建时只需“GcsFileOptions.Builder.contentEncoding(”gzip“)”,我们在阅读文件时遇到以下问题:

com.google.appengine.tools.cloudstorage.NonRetriableException: java.lang.RuntimeException: com.google.appengine.tools.cloudstorage.SimpleGcsInputChannelImpl$1@1c07d21: Unexpected cause of ExecutionException
    at com.google.appengine.tools.cloudstorage.RetryHelper.doRetry(RetryHelper.java:87)
    at com.google.appengine.tools.cloudstorage.RetryHelper.runWithRetries(RetryHelper.java:129)
    at com.google.appengine.tools.cloudstorage.RetryHelper.runWithRetries(RetryHelper.java:123)
    at com.google.appengine.tools.cloudstorage.SimpleGcsInputChannelImpl.read(SimpleGcsInputChannelImpl.java:81)
...


Caused by: java.lang.RuntimeException: com.google.appengine.tools.cloudstorage.SimpleGcsInputChannelImpl$1@1c07d21: Unexpected cause of ExecutionException
    at com.google.appengine.tools.cloudstorage.SimpleGcsInputChannelImpl$1.call(SimpleGcsInputChannelImpl.java:101)
    at com.google.appengine.tools.cloudstorage.SimpleGcsInputChannelImpl$1.call(SimpleGcsInputChannelImpl.java:81)
    at com.google.appengine.tools.cloudstorage.RetryHelper.doRetry(RetryHelper.java:75)
    ... 56 more
Caused by: java.lang.IllegalStateException: com.google.appengine.tools.cloudstorage.oauth.OauthRawGcsService$2@1d8c25d: got 46483 > wanted 19823
    at com.google.common.base.Preconditions.checkState(Preconditions.java:177)
    at com.google.appengine.tools.cloudstorage.oauth.OauthRawGcsService$2.wrap(OauthRawGcsService.java:418)
    at com.google.appengine.tools.cloudstorage.oauth.OauthRawGcsService$2.wrap(OauthRawGcsService.java:398)
    at com.google.appengine.api.utils.FutureWrapper.wrapAndCache(FutureWrapper.java:53)
    at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:90)
    at com.google.appengine.tools.cloudstorage.SimpleGcsInputChannelImpl$1.call(SimpleGcsInputChannelImpl.java:86)
    ... 58 more

使用“gzip”压缩读取文件还应该添加什么才能读取应用引擎中的内容? (来自客户端的curl云存储URL适用于压缩和未压缩文件)

这是适用于未压缩对象的代码:

  byte[] blobContent = new byte[0];

        try
        {
            GcsFileMetadata metaData = gcsService.getMetadata(fileName);
            int fileSize = (int) metaData.getLength();
            final int chunkSize = BlobstoreService.MAX_BLOB_FETCH_SIZE;

            LOG.info("content encoding: " + metaData.getOptions().getContentEncoding()); // "gzip" here
            LOG.info("input size " + fileSize);  // the size is obviously the compressed size!

            for (long offset = 0; offset < fileSize;)
            {
                if (offset != 0)
                {
                    LOG.info("Handling extra size for " + filePath + " at " + offset); 
                }

                final int size = Math.min(chunkSize, fileSize);

                ByteBuffer result = ByteBuffer.allocate(size);
                GcsInputChannel readChannel = gcsService.openReadChannel(fileName, offset);
                try
                {
                    readChannel.read(result);   <<<< here the exception was thrown
                }
                finally
                {
                    ......

现在压缩为:

GcsFilename filename = new GcsFilename(bucketName, filePath);
GcsFileOptions.Builder builder = new GcsFileOptions.Builder().mimeType(image_type);

    builder = builder.contentEncoding("gzip");

    GcsOutputChannel writeChannel = gcsService.createOrReplace(filename, builder.build());

        ByteArrayOutputStream byteStream = new ByteArrayOutputStream(blob_content.length);
        try
        {
            GZIPOutputStream zipStream = new GZIPOutputStream(byteStream);
            try
            {
                zipStream.write(blob_content);
            }
            finally
            {
                zipStream.close();
            }
        }
        finally
        {
            byteStream.close();
        }

        byte[] compressedData = byteStream.toByteArray();
        writeChannel.write(ByteBuffer.wrap(compressedData));

blob_content从46483字节压缩到19823字节。


我认为这是谷歌代码的错误

https://code.google.com/p/appengine-gcs-client/source/browse/trunk/java/src/main/java/com/google/appengine/tools/cloudstorage/oauth/OauthRawGcsService.java,L418:

 Preconditions.checkState(content.length <= want, "%s: got %s > wanted %s", this, content.length, want);

HTTPResponse已经解码了blob,因此这里的Precondition是错误的。

4 个答案:

答案 0 :(得分:0)

如果我理解你必须设置mineType:

GcsFileOptions options = new GcsFileOptions.Builder().mimeType("text/html")

Google云端存储不会压缩或解压缩对象: https://developers.google.com/storage/docs/reference-headers?csw=1#contentencoding

我希望这就是你想要做的。

答案 1 :(得分:0)

查看您的代码,似乎存储的内容与读取的内容之间存在不匹配。该文档指定不会为您执行压缩(https://developers.google.com/storage/docs/reference-headers?csw=1#contentencoding)。您需要手动进行实际压缩。

另外,如果你看一下抛出异常(https://code.google.com/p/appengine-gcs-client/source/browse/trunk/java/src/main/java/com/google/appengine/tools/cloudstorage/oauth/OauthRawGcsService.java?r=81&spec=svn134)的类的实现,你会注意到你得到了原始内容,但实际上你正在期待压缩内容。检查上面提到的类中的方法readObjectAsync。

看起来持久化的内容可能不会被gzip压缩或内容长度设置不正确。您应该做的是在将压缩流写入通道之前验证压缩流的长度。您还应验证在执行http请求时是否正确设置了内容长度。查看实际的http请求标头并确保内容长度标头与http响应中的实际内容长度匹配将很有用。

此外,看起来contentEncoding可能设置不正确。尝试使用此{3}}中使用的.contentEncoding("Content-Encoding: gzip")。虽然最好的办法是检查HTTP请求和响应。您可以使用wireshark轻松完成此操作。

此外,您需要确保GCSOutputChannel在文件完成时关闭。

希望这会让你走上正轨。要抓取您的内容,您可以使用java test

答案 2 :(得分:0)

我看到同样的问题,可以通过使用&#34; gsutil cp -Z&#34;上传文件轻松重现,然后尝试使用以下内容打开它

environment [var] [value]

这会导致如下异常:

ByteArrayOutputStream output = new ByteArrayOutputStream();
try (GcsInputChannel readChannel = svc.openReadChannel(filename, 0)) {
  try (InputStream input = Channels.newInputStream(readChannel))
  {
    IOUtils.copy(input, output);
  }
}

我发现的唯一工作是使用readChannel.read将整个文件读入内存:

java.lang.IllegalStateException:
....oauth.OauthRawGcsService$2@1883798: got 64303 > wanted 4096
at ....Preconditions.checkState(Preconditions.java:199)
at ....oauth.OauthRawGcsService$2.wrap(OauthRawGcsService.java:519)
at ....oauth.OauthRawGcsService$2.wrap(OauthRawGcsService.java:499)

不幸的是,这只有在bytebuffer的大小大于或等于文件的未压缩大小时才有效,这是不可能通过api获得的。

我还发表了对谷歌注册的问题的评论:https://code.google.com/p/googleappengine/issues/detail?id=10445

答案 3 :(得分:0)

这是我读取压缩gzip文件的功能

public byte[] getUpdate(String fileName) throws IOException
{

    GcsFilename fileNameObj = new GcsFilename(defaultBucketName, fileName); 
    try (GcsInputChannel readChannel = gcsService.openReadChannel(fileNameObj, 0))
    {  
        maxSizeBuffer.clear();
        readChannel.read(maxSizeBuffer);
    } 
    byte[] result = maxSizeBuffer.array(); 
    return result;
}

核心是您无法使用已保存文件的大小,因为 Google存储空间会以原始尺寸提供给您,因此它会检查您预期的尺寸和实际尺寸,这些是型动物:

  

Preconditions.checkState(content.length&lt; = want,“%s:得到%s&gt;想要   %s“,this,content.length,want);

所以我解决了使用BlobstoreService.MAX_BLOB_FETCH_SIZE为这些文件分配最大数量的问题。实际上maxSizeBuffer只分配一次超出函数

ByteBuffer maxSizeBuffer = ByteBuffer.allocate(BlobstoreService.MAX_BLOB_FETCH_SIZE);

使用maxSizeBuffer.clear();,所有数据都会再次刷新。