Android:上传视频时出现OutOfMemoryError错误 - 如何最好地分块?

7

我遇到了与这里描述的相同的问题,但是我会提供更多细节。在尝试在Android上传视频时,我将其读入内存,如果视频很大,就会出现OutOfMemoryError。

以下是我的代码:

// get bytestream to upload
videoByteArray = getBytesFromFile(cR, fileUriString);

public static byte[] getBytesFromFile(ContentResolver cR, String fileUriString) throws IOException {
    Uri tempuri = Uri.parse(fileUriString);
    InputStream is = cR.openInputStream(tempuri);
    byte[] b3 = readBytes(is);
    is.close();
    return b3;
}
public static byte[] readBytes(InputStream inputStream) throws IOException {
    ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
    // this is storage overwritten on each iteration with bytes
    int bufferSize = 1024;
    byte[] buffer = new byte[bufferSize];
    int len = 0;
    while ((len = inputStream.read(buffer)) != -1) {
        byteBuffer.write(buffer, 0, len);
    }
    return byteBuffer.toByteArray();
}

以下是回溯信息(错误发生在byteBuffer.write(buffer, 0, len)这一行):

04-08 11:56:20.456: ERROR/dalvikvm-heap(6088): Out of memory on a 16775184-byte allocation.
04-08 11:56:20.456: INFO/dalvikvm(6088): "IntentService[UploadService]" prio=5 tid=17 RUNNABLE
04-08 11:56:20.456: INFO/dalvikvm(6088):   | group="main" sCount=0 dsCount=0 s=N obj=0x449a3cf0 self=0x38d410
04-08 11:56:20.456: INFO/dalvikvm(6088):   | sysTid=6119 nice=0 sched=0/0 cgrp=default handle=4010416
04-08 11:56:20.456: INFO/dalvikvm(6088):   at java.io.ByteArrayOutputStream.expand(ByteArrayOutputStream.java:~93)
04-08 11:56:20.456: INFO/dalvikvm(6088):   at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:218)
04-08 11:56:20.456: INFO/dalvikvm(6088):   at com.android.election2010.UploadService.readBytes(UploadService.java:199)
04-08 11:56:20.456: INFO/dalvikvm(6088):   at com.android.election2010.UploadService.getBytesFromFile(UploadService.java:182)
04-08 11:56:20.456: INFO/dalvikvm(6088):   at com.android.election2010.UploadService.doUploadinBackground(UploadService.java:118)
04-08 11:56:20.456: INFO/dalvikvm(6088):   at com.android.election2010.UploadService.onHandleIntent(UploadService.java:85)
04-08 11:56:20.456: INFO/dalvikvm(6088):   at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:30)
04-08 11:56:20.456: INFO/dalvikvm(6088):   at android.os.Handler.dispatchMessage(Handler.java:99)
04-08 11:56:20.456: INFO/dalvikvm(6088):   at android.os.Looper.loop(Looper.java:123)
04-08 11:56:20.456: INFO/dalvikvm(6088):   at android.os.HandlerThread.run(HandlerThread.java:60)
04-08 11:56:20.467: WARN/dalvikvm(6088): threadid=17: thread exiting with uncaught exception (group=0x4001b180)
04-08 11:56:20.467: ERROR/AndroidRuntime(6088): Uncaught handler: thread IntentService[UploadService] exiting due to uncaught exception
04-08 11:56:20.467: ERROR/AndroidRuntime(6088): java.lang.OutOfMemoryError
04-08 11:56:20.467: ERROR/AndroidRuntime(6088):     at java.io.ByteArrayOutputStream.expand(ByteArrayOutputStream.java:93)
04-08 11:56:20.467: ERROR/AndroidRuntime(6088):     at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:218)
04-08 11:56:20.467: ERROR/AndroidRuntime(6088):     at com.android.election2010.UploadService.readBytes(UploadService.java:199)
04-08 11:56:20.467: ERROR/AndroidRuntime(6088):     at com.android.election2010.UploadService.getBytesFromFile(UploadService.java:182)
04-08 11:56:20.467: ERROR/AndroidRuntime(6088):     at com.android.election2010.UploadService.doUploadinBackground(UploadService.java:118)
04-08 11:56:20.467: ERROR/AndroidRuntime(6088):     at com.android.election2010.UploadService.onHandleIntent(UploadService.java:85)
04-08 11:56:20.467: ERROR/AndroidRuntime(6088):     at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:30)
04-08 11:56:20.467: ERROR/AndroidRuntime(6088):     at android.os.Handler.dispatchMessage(Handler.java:99)
04-08 11:56:20.467: ERROR/AndroidRuntime(6088):     at android.os.Looper.loop(Looper.java:123)
04-08 11:56:20.467: ERROR/AndroidRuntime(6088):     at android.os.HandlerThread.run(HandlerThread.java:60)
04-08 11:56:20.496: INFO/Process(4657): Sending signal. PID: 6088 SIG: 3

我猜 正如@DroidIn所建议的那样, 我需要分块上传。但是(新手问题警报)这是否意味着我应该发出多个PostMethod请求,并在服务器端将文件粘合在一起?还是可以将字节流分块加载到内存中,并在Android代码中将它们粘合在一起?如果有人能给我一个最佳方法的提示,我将非常感激。
4个回答

11

Jim Blackler 是完全正确的。你不能把完整的视频以 byte[] 的形式保存在内存中。因此,你应该直接将文件读取到 HttpUrlConnectio.getOutputStream() 中,而不是 byte[]。

但即使这样,你仍然会遇到 OutOfMemory 问题,因为 HttpUrlConnection 在内存中缓存所有输出数据,并且只有在你完成写入时才将其发送到网络。

要覆盖这种行为,你可以使用 HttpUrlConnection.setChunkedStreamingMode() 或 HttpUrlConnection.setFixedLengthStreamingMode()。如果你的服务器接受流块,你可以使用 setChunkedStreamingMode()。否则,你将不得不使用 setFixedLengthStreamingMode。使用 setFixedLengthStreamingMode,你需要在开始流式传输数据之前知道确切的内容长度,因此有点棘手。

此时就不再有 OutOfMemory 问题了。

如果你的视频已经保存在文件中,你可以选择另一种更简单的方法。像这个 如何发送 HTTP POST 请求并接收响应? 中所示,使用 HttpClient 和 FileBody。我认为它们实现了上述所有方法,你将不必担心这些问题。


请问您能否建议哪种方式最好?目前我正在进行创建数据块并逐个上传的研究。这是一个好的方法吗? - Paresh Mayani
@Fedor 我怎样才能知道我的Apache服务器是否接受流块?我知道如果我设置了setChunkedStreamingMode(),我将不会收到任何数据:https://dev59.com/bXDYa4cB1Zd3GeqPDr1s - HGPB
不再推荐使用HttpClient类,因为它的开发已经停止。请参考以下链接:http://android-developers.blogspot.in/2011/09/androids-http-clients.html - JaydeepW

3

你不需要将整个文件存储在内存中,然后一次性上传。可以调用connection.getOutputStream(),并以块的形式写入字节。


基本上,将readBytes()中的ByteArrayOutputStream替换为与连接相关联的OutputStream - wds

2

小提示:

在Android 2.1中,setChunkedStreamingMode和setFixedeLengthStreamingMode不可用,它们在2.0和2.2中表现完美。


我遇到了相反的情况:setChunkedStreamingMode在2.3.3中有效,但在2.2中无效。看起来我们应该在v.<2.3时使用HttpClient,在2.2以上使用HttpUrlConnection。双重编码:( - Oleksii Malovanyi

0
最好将文件分成几部分上传,然后在服务器端合并这些部分。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接