2010-07-09 60 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