2013-03-11 10 views
19

Sto cercando di implementare la pausa/ripresa nel mio download manager, cerco sul web e leggo diversi articoli e cambio il mio codice in base a loro ma il curriculum sembra non funzionare correttamente, qualche idea?Implementare la pausa/riprendere nel download del file

   if (!downloadPath.exists()) 
        downloadPath.mkdirs(); 

       if (outputFileCache.exists()) 
       { 
        downloadedSize = outputFileCache.length(); 
        connection.setAllowUserInteraction(true); 
        connection.setRequestProperty("Range", "bytes=" + downloadedSize + "-"); 
        connection.setConnectTimeout(14000); 
        connection.connect(); 
        input = new BufferedInputStream(connection.getInputStream()); 
        output = new FileOutputStream(outputFileCache, true); 
        input.skip(downloadedSize); //Skip downloaded size 
       } 
       else 
       { 
        connection.setConnectTimeout(14000); 
        connection.connect(); 
        input = new BufferedInputStream(url.openStream()); 
        output = new FileOutputStream(outputFileCache); 
       } 

       fileLength = connection.getContentLength();     


       byte data[] = new byte[1024]; 
       int count = 0; 
       int __progress = 0; 
       long total = downloadedSize; 

       while ((count = input.read(data)) != -1 && !this.isInterrupted()) 
       { 
        total += count; 
        output.write(data, 0, count); 
        __progress = (int) (total * 100/fileLength); 

       } 
       output.flush(); 
       output.close(); 
       input.close(); 
+1

Che cosa non funziona ?? – BrianPlummer

+0

@BrianPlummer Quando sospendo e riprendo il download, il download inizia dall'inizio e bloccato quando arriva all'ultimo stato in cui lo metto in pausa. – NullPointer

+0

@BrianPlummer Penso che questa riga '(int) (totale * 100/fileLength)' restituisca progressi sbagliati! – NullPointer

risposta

17

problema Ok fisso, qui è il mio codice per gli altri utenti che desiderano implementare pausa/riprendere:

 if (outputFileCache.exists()) 
     { 
      connection.setAllowUserInteraction(true); 
      connection.setRequestProperty("Range", "bytes=" + outputFileCache.length() + "-"); 
     } 

     connection.setConnectTimeout(14000); 
     connection.setReadTimeout(20000); 
     connection.connect(); 

     if (connection.getResponseCode()/100 != 2) 
      throw new Exception("Invalid response code!"); 
     else 
     { 
      String connectionField = connection.getHeaderField("content-range"); 

      if (connectionField != null) 
      { 
       String[] connectionRanges = connectionField.substring("bytes=".length()).split("-"); 
       downloadedSize = Long.valueOf(connectionRanges[0]); 
      } 

      if (connectionField == null && outputFileCache.exists()) 
       outputFileCache.delete(); 

      fileLength = connection.getContentLength() + downloadedSize; 
      input = new BufferedInputStream(connection.getInputStream()); 
      output = new RandomAccessFile(outputFileCache, "rw"); 
      output.seek(downloadedSize); 

      byte data[] = new byte[1024]; 
      int count = 0; 
      int __progress = 0; 

      while ((count = input.read(data, 0, 1024)) != -1 
        && __progress != 100) 
      { 
       downloadedSize += count; 
       output.write(data, 0, count); 
       __progress = (int) ((downloadedSize * 100)/fileLength); 
      } 

      output.close(); 
      input.close(); 
     } 
+0

Perché si imposta 'connection.setAllowUserInteraction (true);'? La documentazione dice: 'Imposta allowUserInteraction. Inutilizzato da Android. ' Perchè ne hai bisogno? – felixd

+1

Qual era il problema attuale? –

+3

@NullPointer, Come si inizializza 'outputFileCache'? –

4

E 'impossibile dire cosa non va senza qualche informazione in più, ma cose da notare:

  1. È necessario fare una/1.1 richiesta HTTP (è difficile dire dal codice di esempio)
  2. il server deve supportare HTTP/1.1
  3. il server vi dirà che cosa sostiene con una Accept-Ranges intestazione nella risposta
  4. Se-Range dovrebbe essere l'ETAG server ti ha dato per la risorsa, Non la data dell'ultima modifica

Si dovrebbe controllare la vostra richiesta gamma con qualcosa di semplice per testare l'origine supporta in realtà la richiesta di intervallo prima (come curl o wget)

1

vorrei iniziare il debug da questa linea:

Come dal codice sorgente non è possibile determinare cosa sia downloadedSize, è difficile elaborare ulteriormente, ma il formato deve essere bytes=from-to.

In ogni caso, suggerisco di utilizzare Apache HttpClient per evitare i problemi più comuni. Here è una domanda di qualcuno che utilizza Apache HttpClient su un argomento simile e viene fornito un codice di esempio.

0

Penso che sia sufficiente eliminare la riga input.skip (downloadSize). Se si imposta l'intestazione HTTP per intervallo di byte, il server salterà inviando tali byte.

Supponiamo che tu abbia un file di 20 byte composto da "aaaaabbbbbcccccddddd" e supponiamo che il trasferimento sia sospeso dopo aver scaricato 5 byte. Quindi l'intestazione Range farà sì che il server invii "bbbbbccccddddd", dovresti leggere tutto il di questo contenuto e aggiungerlo al file - no skip(). Ma la chiamata skip() nel tuo codice salterà "bbbbb" lasciando "cccccdddd" per essere scaricato. Se hai già scaricato almeno il 50% del file, skip() esaurirà tutto l'input e non accadrà nulla.

Inoltre, si applicano tutte le cose nel post di stringy05. È necessario assicurarsi che il server supporta HTTP/1.1, assicurarsi che l'intestazione Range è supportata per la risorsa (contenuti generati dinamicamente potrebbe non supportare esso), e assicurarsi che la risorsa non viene modificata utilizzando ETAG e data di modifica.

+0

Provato ma non ha cambiato nulla. – NullPointer

2

È possibile che il server stia impiegando molto a rispondere (oltre al limite di timeout) o anche che non tutti i server supportano la pausa - riprendi. E 'anche un punto a riflettere che il tempo il file viene scaricato tramite HTTP, HTTPS, FTP o UDP.

La pausa "potrebbe solo significare leggere un po 'del flusso e scriverlo su disco. Quando si riprende, è necessario utilizzare le intestazioni per specificare cosa è rimasto da scaricare.

si può provare qualcosa di simile:

HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 
    if(ISSUE_DOWNLOAD_STATUS.intValue()==ECMConstant.ECM_DOWNLOADING){ 
     File file=new File(DESTINATION_PATH); 
     if(file.exists()){ 
      downloaded = (int) file.length(); 
     connection.setRequestProperty("Range", "bytes="+(file.length())+"-"); 
    } 
}else{ 
    connection.setRequestProperty("Range", "bytes=" + downloaded + "-"); 
} 
connection.setDoInput(true); 
connection.setDoOutput(true); 
progressBar.setMax(connection.getContentLength()); 
in = new BufferedInputStream(connection.getInputStream()); 
fos=(downloaded==0)? new FileOutputStream(DESTINATION_PATH): new FileOutputStream(DESTINATION_PATH,true); 
bout = new BufferedOutputStream(fos, 1024); 
byte[] data = new byte[1024]; 
int x = 0; 
while ((x = in.read(data, 0, 1024)) >= 0) { 
    bout.write(data, 0, x); 
    downloaded += x; 
    progressBar.setProgress(downloaded); 
} 

e provare a sincronizzare le cose.

+0

Non utilizzare 'connection.setDoInput (true);' e 'connection.setDoOutput (true);', causerà un altro problema. – NullPointer

+0

È necessario impostarlo su true se si desidera inviare (output) un corpo di richiesta, ad esempio con richieste POST o PUT. Con GET, di solito non invii un corpo, quindi non ne hai bisogno. E allo stesso modo per ricevere un corpo con la risposta in arrivo è necessario impostare l'input su true. L'invio del corpo della richiesta avviene tramite il flusso di output della connessione: conn.getOutputStream(). Write (someBytes); –

+0

Non penso che '__progress = (int) (totale * 100/fileLength);' sta facendo qualcosa di sbagliato –