无法获取 http POST 文件上传的进度 (Android)
我正在开发一个 Android 应用程序,它使用户能够将文件上传到 Twitpic 等服务。 POST 上传无需任何外部库即可完成,并且工作正常。我唯一的问题是,我无法获取任何进度,因为所有上传都是在我收到响应时完成的,而不是在将字节写入输出流时完成的。 这就是我所做的:
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
然后我将表单数据写入dos,现在这在这里并不那么重要。之后,我写入文件数据本身(从“in”读取,这是我要发送的数据的InputStream):
while ((bytesAvailable = in.available()) > 0)
{
bufferSize = Math.min(bytesAvailable, maxBufferSize);
byte[] buffer = new byte[bufferSize];
bytesRead = in.read(buffer, 0, bufferSize);
dos.write(buffer, 0, bytesRead);
}
之后,我发送多部分表单数据以指示文件结尾。然后,我关闭流:
in.close();
dos.flush();
dos.close();
这一切都工作得很好,到目前为止没有问题。然而,我的问题是,无论文件有多大,到目前为止的整个过程大约需要一两秒。 当我阅读响应时,上传本身似乎发生了:
DataInputStream inStream = new DataInputStream(conn.getInputStream());
这需要几秒钟或几分钟,具体取决于文件有多大以及互联网连接的速度。 我现在的问题是: 1)为什么当我将字节写入“dos”时没有发生上传? 2)当所有事情同时发生时,如何获取进度以便在上传过程中显示进度对话框?
/编辑: 1)我在标头中设置了内容长度,这稍微改变了问题,但在任何情况下都没有 解决方法: 现在,在最后一个字节写入流后,整个内容就会上传。所以,这并不能改变你无法抓取进度的情况,因为同样,数据是一次性写入的。 2)我在Apache HttpClient v4中尝试了MultipartEntity。那里根本没有 OutputStream,因为所有数据都是在执行请求时写入的。话又说回来,没有办法抢进度。
有没有人有其他想法如何在分段/表单上传中获取流程?
I am developing an Android app which enables the user to upload a file to services like Twitpic and others.
The POST upload is done without any external libraries and works just fine. My only problem is, that I can't grab any progress because all the uploading is done when I receive the response, not while writing the bytes into the outputstream.
Here is what I do:
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
Then I write the form data to dos, which is not so important here, now. After that I write the file data itself (reading from "in", which is the InputStream of the data I want to send):
while ((bytesAvailable = in.available()) > 0)
{
bufferSize = Math.min(bytesAvailable, maxBufferSize);
byte[] buffer = new byte[bufferSize];
bytesRead = in.read(buffer, 0, bufferSize);
dos.write(buffer, 0, bytesRead);
}
After that, I send the multipart form data to indicate the end of the file. Then, I close the streams:
in.close();
dos.flush();
dos.close();
This all works perfectly fine, no problem so far. My problem is, however, that the whole process up to this point takes about one or two seconds no matter how large the file is.
The upload itself seems to happen when I read the response:
DataInputStream inStream = new DataInputStream(conn.getInputStream());
This takes several seconds or minutes, depending on how large the file is and how fast the internet connection.
My questions now are:
1) Why doesn't the uplaod happen when I write the bytes to "dos"?
2) How can I grab a progress in order to show a progress dialog during the upload when it all happens at once?
/EDIT:
1) I set the Content-Length in the header which changes the problem a bit, but does not in any
way solve it:
Now the whole content is uploaded after the last byte is written into the stream. So, this doesn't change the situation that you can't grab the progress, because again, the data is written at once.
2) I tried MultipartEntity in Apache HttpClient v4. There you don't have an OutputStream at all, because all the data is written when you perform the request. So again, there is no way to grab a progress.
Is there anyone out there who has any other idea how to grab a process in a multipart/form upload?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在过去的几天里我已经尝试了很多,我想我已经找到了最初问题的答案:
使用 HttpURLConnection 不可能获取进度,因为 Android 中存在错误/异常行为:
http://code.google.com/p/android/ issues/detail?id=3164#c6
它将在 Froyo 后得到修复,这不是人们可以等待的事情......
所以,我发现的唯一其他选择是使用 Apache HttpClient。 papleu 链接的答案是正确的,但它指一般Java。那里使用的类在 Android 中不再可用(HttpClient 3.1 曾经是 SDK 的一部分)。因此,您可以做的是添加 HttpClient 4 中的库(特别是 apache-mime4j-0.6.jar 和 httpmime-4.0.1.jar),并结合使用第一个答案(由 Tuler 提供)和最后一个答案(由 Hamy 提供) ):
这适用于 HttpClient 4,但缺点是我的 apk 现在大小为 235 kb(当我使用问题中描述的分段上传时,大小为 90 kb),更糟糕的是,安装的应用程序大小为 735 kb (之前大约 170 kb)。
那真是太糟糕了。只是为了在上传过程中取得进展,应用程序的大小现在是以前的 4 倍多。
I have tried quite a lot in the last days and I think I have the answer to the initial question:
It's not possible to grab a progress using HttpURLConnection because there is a bug / unusual behavior in Android:
http://code.google.com/p/android/issues/detail?id=3164#c6
It will be fixed post Froyo, which is not something one can wait for...
So, the only other option I found is to use the Apache HttpClient. The answer linked by papleu is correct, but it refers to general Java. The classes that are used there are not available in Android anymore (HttpClient 3.1 was part of the SDK, once). So, what you can do is add the libraries from HttpClient 4 (specifically apache-mime4j-0.6.jar and httpmime-4.0.1.jar) and use a combination of the first answer (by Tuler) and the last answer (by Hamy):
This works with HttpClient 4, but the downside is that my apk now has a size of 235 kb (it was 90 kb when I used the multipart upload described in my question) and, even worse, an installed app size of 735 kb (about 170 kb before).
That's really awful. Only to get a progress during upload the app size is now more than 4 times as big as it was before.
试试这个。有用。
参考:http://delimitry.blogspot.in/2011/08/android -upload-progress.html
Try this out. It works.
Reference: http://delimitry.blogspot.in/2011/08/android-upload-progress.html
可以从 DefaultHttpClient 获取进度。事实上,它会保留在队列中
直到发送完整的数据,但这并不一定意味着您无法获取进度。也就是说,HttpContext 在执行此行时发生变化,因此如果您通过不同的线程访问它,您可能会观察到其中的变化。你需要的是ConnAdapter。它嵌入了 HttpConnectionMetrics
如果您定期检查它,您将观察进度。确保正确处理线程,并保护所有 try/catch。特别是,处理 Context 不再有效的情况 (IllegalStateException),这种情况在 Put 取消或完成时发生。
It is possible to grab the progress from the DefaultHttpClient. The fact that it stays in the line
until complete data is sent, does not necessarily mean that you cannot grab the progress. Namely, HttpContext changes while executing this line, so if you approach it via different Thread, you may observe changes in that. What you need is ConnAdapter. It has HttpConnectionMetrics embedded
If you check this periodically, you will observe the progress. Be sure to correctly handle threads, and all try/catch guarded. Especially, handle the case when Context is not valid any more (IllegalStateException), which occurs when Put is canceled or completed.
请改用 WatchedInputStream。
它很好,很棒,而且很容易使用。
watchedStream.setListener
watchedStream.setListener(new WatchedInputStream.Listener() {
公共无效通知(int读){
// 执行一些操作来更新 UI。
}
}
Use WatchedInputStream instead.
It nice, great, and easy to use.
watchedStream.setListener
watchedStream.setListener(new WatchedInputStream.Listener() {
public void notify(int read) {
// do something to update UI.
}
}