2009-02-23 8 views
68

Ho problemi a scaricare un file binario (video) nella mia app da internet. In Quicktime, se lo scarico direttamente funziona bene, ma attraverso la mia app in qualche modo viene incasinato (anche se sembrano esattamente gli stessi in un editor di testo). Ecco un esempio:Android download problemi di file binari

URL u = new URL("http://www.path.to/a.mp4?video"); 
    HttpURLConnection c = (HttpURLConnection) u.openConnection(); 
    c.setRequestMethod("GET"); 
    c.setDoOutput(true); 
    c.connect(); 
    FileOutputStream f = new FileOutputStream(new File(root,"Video.mp4")); 


    InputStream in = c.getInputStream(); 

    byte[] buffer = new byte[1024]; 
    int len1 = 0; 
    while ((len1 = in.read(buffer)) > 0) { 
     f.write(buffer); 
    } 
    f.close(); 

risposta

91

Non so se è l'unico problema, ma hai un classico problema tecnico Java in là: Non stai contando sul fatto che read() è sempre permesso di restituire meno byte di quello che chiedi. Pertanto, la lettura potrebbe ottenere meno di 1024 byte, ma la scrittura scrive sempre esattamente 1024 byte, inclusi eventualmente i byte dell'iterazione del ciclo precedente.

corretta con:

while ((len1 = in.read(buffer)) > 0) { 
     f.write(buffer,0, len1); 
} 

Forse la messa in rete di latenza più alto o più piccole dimensioni dei pacchetti di 3G su Android stanno esacerbando l'effetto?

+4

Che stupido errore ... grazie! Questo è quello che succede quando non leggi correttamente il tutorial :) –

+3

Grazie ... mi ha aiutato anche io. –

+0

Che ne dici di inizializzare il buffer? Che dire della protezione contro le eccezioni? Che dire del rilascio delle risorse? Penso che sia una risposta buona ma non completa. Ci sono altre risposte più complete qui. –

16

Un problema è la lettura del buffer. Se ogni lettura del flusso di input non è un multiplo esatto di 1024, i dati errati vengono copiati. Usa:

byte[] buffer = new byte[1024]; 
int len1 = 0; 
while ((len1 = in.read(buffer)) != -1) { 
    f.write(buffer,0, len1); 
} 
+0

Nella riga 4, cosa vuoi dire len1, non len? –

+0

Guardo l'esempio di Ry4an e presumo tu intenda len1 - grazie. –

14
public class download extends Activity { 

    private static String fileName = "file.3gp"; 
    private static final String MY_URL = "Your download url goes here"; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

     try { 
      URL url = new URL(MY_URL); 
      HttpURLConnection c = (HttpURLConnection) url.openConnection(); 
      c.setRequestMethod("GET"); 
      c.setDoOutput(true); 
      c.connect(); 

      String PATH = Environment.getExternalStorageDirectory() 
       + "/download/"; 
      Log.d("Abhan", "PATH: " + PATH); 
      File file = new File(PATH); 
      if(!file.exists()) { 
       file.mkdirs(); 
      } 
      File outputFile = new File(file, fileName); 
      FileOutputStream fos = new FileOutputStream(outputFile); 
      InputStream is = c.getInputStream(); 
      byte[] buffer = new byte[1024]; 
      int len1 = 0; 
      while ((len1 = is.read(buffer)) != -1) { 
       fos.write(buffer, 0, len1); 
      } 
      fos.flush(); 
      fos.close(); 
      is.close(); 
     } catch (IOException e) { 
      Log.e("Abhan", "Error: " + e); 
     } 
     Log.i("Abhan", "Check Your File."); 
    } 
} 
+0

Questa risposta non funzionerà. Le connessioni di rete sul thread principale generano 'android.os.NetworkOnMainThreadException'. – JBirdVegas

+0

@JBirdVegas Non eseguire operazioni relative alla rete sul thread principale. Si prega di creare un thread di lavoro. –

+0

Utilizzare un doInBackground AsyncTask per eseguire il codice di cattura try {}. e rimuovere setDoOutput (true) da esso. – Nepster

2

Basta usare il metodo di copia di apache (Apache Commons IO) - il vantaggio di utilizzare Java!

IOUtils.copy(is, os); 

Non dimenticare di chiudere i flussi in un blocco finally:

try{ 
     ... 
} finally { 
    IOUtils.closeQuietly(is); 
    IOUtils.closeQuietly(os); 
} 
+2

E non attraversare i flussi, neanche. –

+2

Una libreria da 200k solo per scaricare un file ..... –

4

ho fissato il codice sulla base di precedenti commenti su questo thread. Ho testato utilizzando eclipse e più file di grandi dimensioni. Funziona bene. Basta copiare e incollare questo nel proprio ambiente e modificare il percorso http e il percorso in cui si desidera scaricare il file.

try { 
    //this is the file you want to download from the remote server 
    String path ="http://localhost:8080/somefile.zip"; 
    //this is the name of the local file you will create 
    String targetFileName 
     boolean eof = false; 
    URL u = new URL(path); 
    HttpURLConnection c = (HttpURLConnection) u.openConnection(); 
    c.setRequestMethod("GET"); 
    c.setDoOutput(true); 
    c.connect(); 
    FileOutputStream f = new FileOutputStream(new File("c:\\junk\\"+targetFileName)); 
     InputStream in = c.getInputStream(); 
     byte[] buffer = new byte[1024]; 
     int len1 = 0; 
     while ((len1 = in.read(buffer)) > 0) { 
     f.write(buffer,0, len1); 
       } 
    f.close(); 
    } catch (MalformedURLException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
    } catch (ProtocolException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
    } catch (FileNotFoundException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
    } catch (IOException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
} 

Buona fortuna Alireza Aghamohammadi

+0

in questo modo, verrà scaricato lo stesso file. Voglio dire se il file è già scaricato darà un avviso? – Loshi

+0

remove setDoOutput (true); dal codice – Nepster

28
new DefaultHttpClient().execute(new HttpGet("http://www.path.to/a.mp4?video")) 
     .getEntity().writeTo(
       new FileOutputStream(new File(root,"Video.mp4"))); 
+3

soluzione a una linea. Nice – Santhosh

+1

Mi piace anche una soluzione di linea. Tuttavia, dovresti controllare l'entità prima di scrivere su file, altrimenti il ​​file verrà creato anche se c'è un problema con il download. Quindi la prossima volta potresti tentare di aprire un file danneggiato. – thanhbinh84

+0

Come posso denominare dinamicamente il file scaricato come il nome del file originale? –