Android download binary file problems

JavaAndroidDownloadHttpurlconnectionFileoutputstream

Java Problem Overview


I am having problems downloading a binary file (video) in my app from the internet. In Quicktime, If I download it directly it works fine but through my app somehow it get's messed up (even though they look exactly the same in a text editor). Here is a example:

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

Java Solutions


Solution 1 - Java

I don't know if it's the only problem, but you've got a classic Java glitch in there: You're not counting on the fact that read() is always allowed to return fewer bytes than you ask for. Thus, your read could get less than 1024 bytes but your write always writes out exactly 1024 bytes possibly including bytes from the previous loop iteration.

Correct with:

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

Perhaps the higher latency networking or smaller packet sizes of 3G on Android are exacerbating the effect?

Solution 2 - Java

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

Solution 3 - Java

One problem is your reading of the buffer. If every read of the input stream is not an exact multiple of 1024 you will copy bad data. Use:

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

Solution 4 - Java

 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.");
    } 
}

Solution 5 - Java

I fixed the code based on previous feedbacks on this thread. I tested using eclipse and multiple large files. It is working fine. Just have to copy and paste this to your environment and change the http path and the location which you would like the file to be downloaded to.

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

Good luck Alireza Aghamohammadi

Solution 6 - Java

Just use apache's copy method (Apache Commons IO) - the advantage of using Java!

IOUtils.copy(is, os);

Do not forget to close the streams in a finally block:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIsaac WallerView Question on Stackoverflow
Solution 1 - JavaRy4an BraseView Answer on Stackoverflow
Solution 2 - Javanjzk2View Answer on Stackoverflow
Solution 3 - JavaClintView Answer on Stackoverflow
Solution 4 - Javauser456118View Answer on Stackoverflow
Solution 5 - JavagrepitView Answer on Stackoverflow
Solution 6 - JavaAlikElzin-kilakaView Answer on Stackoverflow