2010-07-09 71 views
17

我正在开发一个Android应用程序,它使用户能够将文件上传到像Twitpic和其他服务。 POST上传完成时没有任何外部库,工作得很好。我唯一的问题是,我不能抓住任何进展,因为所有的上传完成时,我收到的回应,而不是写入字节到输出流。 这里是我做的:无法抓取HTTP POST文件上传(Android)的进度

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,这是不那么重要的这里,现在。之后,我写的文件数据本身(从“中”,这是我要发送的数据的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)I组中改变问题了一下头中的Content-Length,但不以任何 的方式解决这个问题: 现在整个内容的最后一个字节写入后上传流。所以,这不会改变你无法获取进度的情况,因为数据再次被写入。 2)我尝试了Apache HttpClient v4中的MultipartEntity。在那里你根本没有OutputStream,因为当你执行请求时,所有的数据都被写入。再次,无法取得进展。

有没有人有任何其他想法如何抓住多部分/形式上传过程?

+0

也许这个答案会给你一些指点:http://stackoverflow.com/questions/254719/file-upload-with-java-with-progress-bar/470047#470047 – pableu 2010-07-09 15:20:28

+0

@pableu :我研究过这个。问题是:Android SDK中没有RequestEntity。它是Apache HttpClient 3.1的一部分,它不再被Android使用。现在,我甚至试图加载新的4.0.1库,但我看不到在这里获得进展的可能性。 还有什么想法? – Manuel 2010-07-12 15:50:11

回答

20

我已经尝试了很多在最后的日子里,我想我有答案了最初的问题:

这是不可能使用HttpURLConnection的,因为在Android的一个bug /不寻常的行为抢进度: 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)添加库。罐子),并使用第一个答案(由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倍以上。

+1

关于.apk和安装大小,你有没有在SourceForge上签过ProGuard?它会缩小你的应用程序回到你所在的位置,甚至更小:) – Tobias 2010-12-01 15:58:35

+1

这是一个梦幻般的解决方案!非常感谢您发布 – 2010-12-08 20:23:21

+0

您不再需要apache-mime4j-0.6.jar,因为在版本4.1 alpha 2之后,apache-mime4j-0.6.jar的依赖关系被删除。 – CSchulz 2011-06-17 16:08:17

0

可以从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时取消或完成Put时发生的情况。

0

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

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

    watchedStream.setListener(新WatchedInputStream.Listener(){

    公共无效的通知(INT读){// 做一些更新UI。
    }}

1

尝试了这一点,它的工作原理。

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