【问题标题】:Android AWS s3 SDK download throws "Socket is closed" exception or terminates earlyAndroid AWS s3 SDK 下载引发“套接字已关闭”异常或提前终止
【发布时间】:2012-01-25 20:16:17
【问题描述】:

我正在尝试使用 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 closed”,要么不报告错误,但只读取对象的一部分。

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

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

这发生在我的 Android 2.3.3 设备和 3.2 设备上。它发生在 WiFi 和 3G 上。

有什么想法吗?

ps> 物体大约 250k 大

【问题讨论】:

    标签: android amazon-s3


    【解决方案1】:

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

    bug的流程如下:

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

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

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

    【讨论】:

    • 从上面链接的线程中不清楚我们是否还需要对适当的 s3Object 进行引用。
    • 已经有一段时间了,但从记忆中我认为最终证明这就是问题所在。
    【解决方案2】:

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

    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 不同的线程上执行此操作。

    祝你好运,

    【讨论】:

    • 谢谢,但我认为这不是我的问题。查看我的代码,它会正确关闭输入流,即使在第一次下载时也会发生错误(在关闭任何流之前)。我有一个解决我的问题的方法,但它非常粗略:当它发生时捕获异常并重新开始下载文件。最终它会成功。
    【解决方案3】:

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

    How to write an S3 object to a file?

    我会尝试像上面问题中的答案那样循环播放内容。缓冲很好,但是....

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

    【讨论】:

    • 谢谢,但链接的帖子是关于一个稍微不同的问题,它的解决方案对我的情况没有帮助。 IOUtil 或多或少与答案中的代码做同样的事情。唯一的区别是缓冲区大小和线程中断处理。在我的情况下,线程中断不是问题,缓冲区大小(1k 与 4k)应该没有区别。
    【解决方案4】:

    无需继续重新初始化 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);
    

    【讨论】:

      猜你喜欢
      • 2014-11-19
      • 2014-03-01
      • 2017-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-29
      • 2020-08-03
      相关资源
      最近更新 更多