2009-02-23 25 views
68

Tengo problemas para descargar un archivo binario (video) en mi aplicación de internet. En Quicktime, si lo descargo directamente, funciona bien, pero a través de mi aplicación, de alguna manera, se arruina (aunque se ve exactamente igual en un editor de texto). He aquí un ejemplo:Descargar archivos binarios con problemas de archivos

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(); 

Respuesta

91

No sé si es el único problema, pero tienes un problema técnico clásico de Java allí: Usted no está contando con el hecho de que leer() es siempre permitió Devuelve menos bytes de los que pides. Por lo tanto, su lectura podría obtener menos de 1024 bytes, pero su escritura siempre escribe exactamente 1024 bytes, posiblemente incluyendo bytes de la iteración del bucle anterior.

correcta con:

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

Quizás la más alta de redes de latencia o tamaño de los paquetes más pequeños de 3G en Android están exacerbando el efecto?

+4

¡Qué error tan estúpido ... gracias! Esto es lo que sucede cuando no lee el tutorial correctamente :) –

+3

Gracias ... me ayudó también. –

+0

¿Qué hay de inicializar el búfer? ¿Qué hay de proteger contra la excepción? ¿Qué pasa con la liberación de los recursos? Creo que es una buena pero no una respuesta completa. Hay otras respuestas más completas aquí. –

16

Uno de los problemas es su lectura de la memoria intermedia. Si cada lectura de la secuencia de entrada no es un múltiplo exacto de 1024, se copiarán los datos incorrectos. Uso:

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

En la línea 4, ¿quiere decir LEN1, no len? –

+0

Miro el ejemplo de Ry4an y supongo que te refieres a len1, gracias. –

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

Esta respuesta no funcionará. Las conexiones de red en el hilo principal lanzarán 'android.os.NetworkOnMainThreadException'. – JBirdVegas

+0

@JBirdVegas No ejecute la operación relacionada con la red en el hilo principal. Amablemente crea un hilo de trabajo. –

+0

Use un DoInBackground de AsyncTask para hacer el código de captura try {}. y elimine setDoOutput (verdadero) de él. – Nepster

2

Simplemente use el método de copia de apache (Apache Commons IO) - ¡la ventaja de usar Java!

IOUtils.copy(is, os); 

No se olvide de cerrar las corrientes en un bloque finally:

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

Y tampoco cruce las corrientes. –

+2

Una biblioteca de 200k solo para descargar un archivo ..... –

4

He arreglado el código basado en evaluaciones anteriores en este hilo. Probé usando eclipse y múltiples archivos grandes. Está funcionando bien. Simplemente tiene que copiar y pegar esto en su entorno y cambiar la ruta http y la ubicación en la que desea que se descargue el archivo.

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(); 
} 

Buena suerte Alireza Aghamohamadi

+0

de esta manera, se descargará el mismo archivo. quiero decir, si el archivo ya está descargado, ¿dará una alerta? – Loshi

+0

eliminar setDoOutput (verdadero); desde el código – Nepster

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

solución de una línea. Nice – Santhosh

+1

También me gusta una solución de línea. Sin embargo, debe verificar la entidad antes de escribir en el archivo, de lo contrario, el archivo se creará incluso si hay un problema con la descarga. Entonces, la próxima vez, puede intentar abrir un archivo dañado. – thanhbinh84

+0

¿Cómo puedo nombrar el archivo descargado igual que el nombre del archivo original de forma dinámica? –

Cuestiones relacionadas