Android AWS s3 SDK 下载抛出“套接字已关闭”错误例外或提前终止

发布于 2024-12-22 05:13:28 字数 845 浏览 0 评论 0原文

我正在尝试使用 AWS Android SDK 1.0.4 或 1.0.3 从 S3 下载对象。

这是我的代码:

AmazonS3Client client = getConnection(userCredentials);
S3Object obj = client.getObject(workspaceName, objectName);
ObjectMetadata meta = obj.getObjectMetadata();
long size = meta.getContentLength();
logger.info("S3 object length: "+size);
InputStream is = new BufferedInputStream(obj.getObjectContent());
byte[] fragmentBytes = IOUtils.toByteArray(is);
logger.info("Read bytes: "+ fragmentBytes.length);

有时(很少)有效。大多数情况下,要么抛出“java.net.SocketException:Socket is close”,要么不报告错误,但只读取对象的一部分。

请注意,BufferedInputStream 对于 IOUtils.toByteArray(...) 来说不是必需的,并且它存在或不存在也没有什么区别。

在调试器中逐行单步执行代码时似乎不会出现该问题。

我的 Android 2.3.3 设备和 3.2 设备上会发生这种情况。它通过 WiFi 和 3G 进行。

有什么想法吗?

附注>这些物体大约有 250k 大

I'm trying to download an object from S3 using the AWS Android SDK 1.0.4 or 1.0.3.

This is my code:

AmazonS3Client client = getConnection(userCredentials);
S3Object obj = client.getObject(workspaceName, objectName);
ObjectMetadata meta = obj.getObjectMetadata();
long size = meta.getContentLength();
logger.info("S3 object length: "+size);
InputStream is = new BufferedInputStream(obj.getObjectContent());
byte[] fragmentBytes = IOUtils.toByteArray(is);
logger.info("Read bytes: "+ fragmentBytes.length);

This sometimes, rarely, works. Most of the time either an "java.net.SocketException: Socket is closed" is thrown or no error is reported but only part of the object is read.

Note, that the BufferedInputStream should not be necessary for IOUtils.toByteArray(...) and it makes no difference if it is there or not.

The problem does not seem to occur when stepping through the code line by line in a debugger.

This happens on my Android 2.3.3 device and 3.2 device. It happens over WiFi and 3G.

Any ideas?

ps> The objects are about 250k big

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

但可醉心 2024-12-29 05:13:28

问题是客户端正在被垃圾收集。要修复此问题,您需要保留对 S3Client 的显式引用。

该错误的流程如下:

  1. 程序获取 S3 客户端
  2. 程序从客户端获取 S3 对象。
  3. 程序从 s3Object 获取 inputStream,s3Object 通过客户端获取。
  4. 垃圾收集运行,并且由于没有对客户端的引用,因此它会收集垃圾,随机关闭您的输入流。

IMO,这是亚马逊的一个非常严重的错误。为什么 S3 对象输入流没有返回客户端的引用对我来说是一个谜。

以下是 AWS 论坛上的主题:https://forums.aws.amazon.com /thread.jspa?threadID=83326

The issue is that the client is being Garbage Collected. To fix it, you need to keep an explicit reference to the S3Client.

The flow of the bug is as follows:

  1. The program gets an S3 client
  2. The program gets a S3 object from the client.
  3. The program gets an inputStream from the s3Object, which gets it through the client.
  4. Garbage collection runs, and since there is no reference to the client, it gets Garbage Collected, randomly closing your inputStream.

IMO, this is a very bad bug by Amazon. Why the S3 object input stream does not have a reference back to the client is a mystery to me.

Here's the thread on the AWS forums: https://forums.aws.amazon.com/thread.jspa?threadID=83326

甜柠檬 2024-12-29 05:13:28

您可能已经解决了这个问题,但我找不到正确的答案,最终自己解决了这个问题,所以解决方案如下:

getObjectContent 方法文档非常清楚有关关闭 InputStream 的信息:

S3ObjectInputStream com.amazonaws.services.s3.model.S3Object.getObjectContent()

获取包含该对象内容的输入流。调用者应尽快关闭此输入流,因为对象内容不会在内存中缓冲并直接从 Amazon S3 进行流式传输。

您犯了与我使用 InputStream (S3Object) 相同的错误,就好像闭包是透明的一样。

我猜您需要从 S3 获取图像才能在 Android 上使用,因此这里有一个返回位图的示例方法。它确实适用于任何类型的输入。

private Bitmap getFromStore(String fileName) {
    AmazonS3Client client = new AmazonS3Client(new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY));

    S3Object o = client.getObject(BUCKET, fileName);
    InputStream is = o.getObjectContent();

    Bitmap image = null;

    try {   
        // Use the inputStream and close it after.
        image = BitmapFactory.decodeStream(is);
    } finally {
        try {
            is.close();
        } catch (IOException e) {
            Log.w(TAG, "Error trying to close image stream. ", e);
        }
    }

    return image;
}

以防万一,不要忘记在与 UI 不同的线程上执行此操作。

祝你好运,

You probably already solved this but I could not find the correct answer and ended up figuring it out by myself, so here goes the solution:

The getObjectContent methods documentation is very clear about closing the InputStream:

S3ObjectInputStream com.amazonaws.services.s3.model.S3Object.getObjectContent()

Gets an input stream containing the contents of this object. Callers should close this input stream as soon as possible, because the object contents aren't buffered in memory and stream directly from Amazon S3.

You are making the same mistake as I was of using the InputStream (S3Object) as if closure was transparent.

I am guessing you need to get an image from S3 to be used on Android, so here goes an example method that returns a Bitmap. It really applies to any kind of input.

private Bitmap getFromStore(String fileName) {
    AmazonS3Client client = new AmazonS3Client(new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY));

    S3Object o = client.getObject(BUCKET, fileName);
    InputStream is = o.getObjectContent();

    Bitmap image = null;

    try {   
        // Use the inputStream and close it after.
        image = BitmapFactory.decodeStream(is);
    } finally {
        try {
            is.close();
        } catch (IOException e) {
            Log.w(TAG, "Error trying to close image stream. ", e);
        }
    }

    return image;
}

Just in case, dont forget to execute this on a different thread then the UI.

Good luck,

何必那么矫情 2024-12-29 05:13:28

嗯,看来你并不孤单。帖子中接受的答案谈到了您的问题。

如何将 S3 对象写入文件?

我会尝试循环内容,就像上面问题的答案一样。 Buffered 很好,但是……

你也可以尝试不使用 IOUtil,毕竟 java 中还有其他选项。

Well, it seems you are not alone. The accepted in the post answer talks of your problem.

How to write an S3 object to a file?

I would try looping the content like the answer in the question above. Buffered is good, but....

You could also try not using the IOUtil, after all there are other options in java.

末が日狂欢 2024-12-29 05:13:28

无需不断重新初始化 s3。

onCreate 中,调用初始化 s3Objects3Client

然后在您的asynctask中使用该调用。这样,您的 s3Client 将保留相同的数据,并且在执行 while 读取时永远不会关闭与 s3 的套接字连接。

S3Client s3Client;
S3Object s3Object;

onCreate() {
     s3Client = new AmazonS3Client(new BasicSessionCredentials(
                  Constants.ACCESS_KEY_ID, Constants.SECRET_KEY, Constants.TOKEN
                ));
     object = new S3Object();
}

doinbackground() {
     object = s3Client.getObject(new GetObjectRequest(Constants.getBucket(), id +".png"));
}

/****** add this in your onCreate ******/
AmazonS3Client client = getConnection(userCredentials);

no need to keep re-initializing s3.

In your onCreate, make the call to initialize s3Object and s3Client.

Then use the call in your asynctask. This way, your s3Client will keep the same data and never close the socket connection with s3 when doing the while read.

S3Client s3Client;
S3Object s3Object;

onCreate() {
     s3Client = new AmazonS3Client(new BasicSessionCredentials(
                  Constants.ACCESS_KEY_ID, Constants.SECRET_KEY, Constants.TOKEN
                ));
     object = new S3Object();
}

doinbackground() {
     object = s3Client.getObject(new GetObjectRequest(Constants.getBucket(), id +".png"));
}

/****** add this in your onCreate ******/
AmazonS3Client client = getConnection(userCredentials);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文