【问题标题】:Can't grab progress on http POST file upload (Android)无法获取 http POST 文件上传的进度(Android)
【发布时间】:2011-03-13 22:28:13
【问题描述】:

我正在开发一个 Android 应用程序,它使用户能够将文件上传到 Twitpic 等服务。 POST 上传是在没有任何外部库的情况下完成的,并且工作正常。我唯一的问题是,我无法取得任何进展,因为所有上传都是在我 receive 响应时完成的,而不是在 将字节写入输出流时完成。 这是我的工作:

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”时不会发生uplaod? 2) 我如何获取进度以便在上传过程中同时显示进度对话框?

/编辑: 1)我在标题中设置了 Content-Length ,这稍微改变了问题,但没有任何 解决方法: 现在整个内容在最后一个字节写入流后上传。所以,这并没有改变你无法抓取进度的情况,因为数据是一次写入的。 2)我在 Apache HttpClient v4 中尝试了 MultipartEntity。那里根本没有 OutputStream,因为所有数据都是在执行请求时写入的。再说一遍,没有办法抢到进度。

有没有人知道如何在多部分/表单上传中获取进程?

【问题讨论】:

  • 也许这个答案会给你一些指点:stackoverflow.com/questions/254719/…
  • @pableu:我已经调查过了。问题是:Android SDK 中没有 RequestEntity。它是 Apache HttpClient 3.1 的一部分,Android 不再使用它。现在,我什至尝试加载新的 4.0.1 库,但我认为这里不可能取得进展。还有什么想法吗?

标签: android http post upload progress


【解决方案1】:

最近几天我尝试了很多,我想我已经回答了最初的问题:

无法使用 HttpURLConnection 获取进度,因为 Android 中存在错误/异常行为: http://code.google.com/p/android/issues/detail?id=3164#c6

它将在 Froyo 之后得到修复,这不是人们可以等待的......

所以,我发现的唯一其他选择是使用 Apache HttpClient。 answer linked by papleu 是正确的,但它指的是通用 Java。那里使用的类在 Android 中不再可用(HttpClient 3.1 曾经是 SDK 的一部分)。因此,您可以做的是添加来自 HttpClient 4 的库(特别是 apache-mime4j-0.6.jar 和 httpmime-4.0.1.jar),并结合使用第一个答案(由 Tuler)和最后一个答案(由 Hamy ):

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;

public class CountingMultipartEntity extends MultipartEntity {

    private final ProgressListener listener;

    public CountingMultipartEntity(final ProgressListener listener) {
        super();
        this.listener = listener;
    }

    public CountingMultipartEntity(final HttpMultipartMode mode, final ProgressListener listener) {
        super(mode);
        this.listener = listener;
    }

    public CountingMultipartEntity(HttpMultipartMode mode, final String boundary,
            final Charset charset, final ProgressListener listener) {
        super(mode, boundary, charset);
        this.listener = listener;
    }

    @Override
    public void writeTo(final OutputStream outstream) throws IOException {
        super.writeTo(new CountingOutputStream(outstream, this.listener));
    }

    public static interface ProgressListener {
        void transferred(long num);
    }

    public static class CountingOutputStream extends FilterOutputStream {

        private final ProgressListener listener;
        private long transferred;

        public CountingOutputStream(final OutputStream out,
                final ProgressListener listener) {
            super(out);
            this.listener = listener;
            this.transferred = 0;
        }


        public void write(byte[] b, int off, int len) throws IOException {
            out.write(b, off, len);
            this.transferred += len;
            this.listener.transferred(this.transferred);
        }

        public void write(int b) throws IOException {
            out.write(b);
            this.transferred++;
            this.listener.transferred(this.transferred);
        }
    }
}

这适用于 HttpClient 4,但缺点是我的 apk 现在的大小为 235 kb(当我使用问题中描述的分段上传时为 90 kb),更糟糕的是,安装的应用程序大小为 735 kb(之前大约 170 kb)。 这真的很糟糕。只是为了在上传过程中取得进展,应用大小现在是以前的 4 倍多。

【讨论】:

  • 关于 .apk 和安装大小,您是否检查过 SourceForge 的 ProGuard?它会将您的应用程序缩小到原来的位置甚至更小:)
  • 这是一个绝妙的解决方案!非常感谢发帖
  • 您不再需要 apache-mime4j-0.6.jar,因为在 4.1 alpha 2 及以后的版本中,apache-mime4j-0.6.jar 的依赖项已被删除。
  • 它不能正确地与 InputStreamBody 一起使用。字节数与原始文件不匹配。
【解决方案2】:

可以从 DefaultHttpClient 中获取进度。事实上,它留在这条线上

response = defaultHttpClient.execute( httpput, context );

在发送完整数据之前,并不一定意味着您无法获取进度。也就是说,HttpContext 在执行此行时会发生变化,因此如果您通过不同的线程接近它,您可能会观察到其中的变化。你需要的是 ConnAdapter。它嵌入了 HttpConnectionMetrics

final AbstractClientConnAdapter connAdapter = (AbstractClientConnAdapter) context.getAttribute(ExecutionContext.HTTP_CONNECTION);
final HttpConnectionMetrics metrics = connAdapter.getMetrics();
int bytesSent = (int)metrics.getSentBytesCount();

如果您定期检查,您将观察到进度。确保正确处理线程,并且所有的 try/catch 都受到保护。特别是 Context 不再有效(IllegalStateException)的情况,在 Put 被取消或完成时发生。

【讨论】:

    【解决方案3】:

    改用 WatchedInputStream。 它很好,很棒,而且易于使用。

    1. 使用 WatchedInputStream 包装您的输入流。
    2. watchedStream.setListener

      watchedStream.setListener(new WatchedInputStream.Listener() {

      公共无效通知(int读取){ // 更新 UI。
      } }

    【讨论】:

      【解决方案4】:

      试试这个。它有效。

      connection = (HttpURLConnection) url_stripped.openConnection();
          connection.setRequestMethod("PUT");
          String boundary = "---------------------------boundary";
          String tail = "\r\n--" + boundary + "--\r\n";
          connection.addRequestProperty("Content-Type", "image/jpeg");
          connection.setRequestProperty("Connection", "Keep-Alive");
          connection.setRequestProperty("Content-Length", ""
                  + file.length());
          connection.setDoOutput(true);
      
          String metadataPart = "--"
                  + boundary
                  + "\r\n"
                  + "Content-Disposition: form-data; name=\"metadata\"\r\n\r\n"
                  + "" + "\r\n";
      
          String fileHeader1 = "--"
                  + boundary
                  + "\r\n"
                  + "Content-Disposition: form-data; name=\"uploadfile\"; filename=\""
                  + fileName + "\"\r\n"
                  + "Content-Type: application/octet-stream\r\n"
                  + "Content-Transfer-Encoding: binary\r\n";
      
          long fileLength = file.length() + tail.length();
          String fileHeader2 = "Content-length: " + fileLength + "\r\n";
          String fileHeader = fileHeader1 + fileHeader2 + "\r\n";
          String stringData = metadataPart + fileHeader;
      
          long requestLength = stringData.length() + fileLength;
          connection.setRequestProperty("Content-length", ""
                  + requestLength);
          connection.setFixedLengthStreamingMode((int) requestLength);
          connection.connect();
      
          DataOutputStream out = new DataOutputStream(
                  connection.getOutputStream());
          out.writeBytes(stringData);
          out.flush();
      
          int progress = 0;
          int bytesRead = 0;
          byte buf[] = new byte[1024];
          BufferedInputStream bufInput = new BufferedInputStream(
                  new FileInputStream(file));
          while ((bytesRead = bufInput.read(buf)) != -1) {
              // write output
              out.write(buf, 0, bytesRead);
              out.flush();
              progress += bytesRead;
              // update progress bar
              publishProgress(progress);
          }
      
          // Write closing boundary and close stream
          out.writeBytes(tail);
          out.flush();
          out.close();
      
          // Get server response
          BufferedReader reader = new BufferedReader(
                  new InputStreamReader(connection.getInputStream()));
          String line = "";
          StringBuilder builder = new StringBuilder();
          while ((line = reader.readLine()) != null) {
              builder.append(line);
          }
      

      参考:http://delimitry.blogspot.in/2011/08/android-upload-progress.html

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-09-22
        • 1970-01-01
        • 2012-04-08
        • 1970-01-01
        • 2012-08-22
        • 2011-07-22
        • 1970-01-01
        相关资源
        最近更新 更多