Android download binary file problems - java

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

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?

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

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

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

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

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

Related

How to download a remote file using Java

I'm trying to download a single file from a web server (http or https) using as few third party libraries as possible.
The method I've come up with is as follows:
private static final int BUFFER_SIZE = 8;
public static boolean download(URL url, File f) throws IOException {
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
FileOutputStream out = new FileOutputStream(f);
BufferedInputStream in = new BufferedInputStream(conn.getInputStream());
byte[] buffer;
long dld = 0, expected = conn.getContentLengthLong(); // TODO expected will be -1 if the content length is unknown
while (true) { // TODO fix endless loop if server timeout
buffer = new byte[BUFFER_SIZE];
int n = in.read(buffer);
if (n == -1) break;
else dld += n;
out.write(buffer);
}
out.close();
System.out.println(dld + "B transmitted to " + f.getAbsolutePath());
return true;
}
However, it does by no means work as intended. I tried to download https://upload.wikimedia.org/wikipedia/commons/6/6d/Rubber_Duck_Florentijn_Hofman_Hong_Kong_2013d.jpg for example, the result was horrifying:
For some reason I was able to view the picture in IrfanView but not in any other viewer, so this is a re saved version.
I tried messing with the buffer size or downloading other images but the results are more or less the same.
If I look at the file, there are entire parts of the content simply replaced with dots:
I'm really lost on this one so thanks for any help :)
The problem occurs when there aren't 8 bytes of data to read. This leaves part of the array filled with zeros, which is why you're seeing so many in your hex editor. The solution is simple: replace out.write(buffer); with out.write(buffer, 0, n);. This tells the FileOutputStream to only read the bytes between indexes 0 and n.
Fixed code:
private static final int BUFFER_SIZE = 8;
public static boolean download(URL url, File f) throws IOException {
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
FileOutputStream out = new FileOutputStream(f);
BufferedInputStream in = new BufferedInputStream(conn.getInputStream());
// We can move the buffer declaration outside the loop
byte[] buffer = new byte[BUFFER_SIZE];
long dld = 0, expected = conn.getContentLengthLong(); // TODO expected will be -1 if the content length is unknown
while (true) {
int n = in.read(buffer);
if (n == -1) break;
else dld += n;
out.write(buffer, 0, n);
}
out.close();
System.out.println(dld + "B transmitted to " + f.getAbsolutePath());
return true;
}
Try something like this to download pictures
public static byte[] download(String param) throws IOException {
InputStream in = null;
ByteArrayOutputStream out = null;
try {
URL url = new URL(param);
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setConnectTimeout(120000);
con.setReadTimeout(120000);
con.setRequestMethod("GET");
con.connect();
in = new BufferedInputStream(con.getInputStream());
out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int n = 0;
while (-1 != (n = in.read(buf))) {
out.write(buf, 0, n);
}
return out.toByteArray();
} finally {
try {
out.close();
} catch (Exception e1) {
}
try {
in.close();
} catch (Exception e2) {
}
}
}

Request Method GET returns wrong content length

Hi i am using an HttpURLConnection that gets a txt file's content and i want to know the size of that file and i use the content length Method but it returns wrong value for example in this code the file's size is 17509 but it returns 5147 ?
so Any Help?
Thanks so much in advance :).
new Thread() {
#Override
public void run() {
String path = parser.getValue(e, "txt");
URL u = null;
try {
u = new URL(path);
HttpURLConnection c = (HttpURLConnection) u
.openConnection();
c.setRequestMethod("GET");
c.connect();
int lenghtOfFile = c.getContentLength();
InputStream in = c.getInputStream();
final ByteArrayOutputStream bo = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
long total = 0;
Log.i("p1",""+lenghtOfFile);
while ((count = in.read(buffer)) != -1) {
total += count;
Log.i("p2",""+total);
bo.write(buffer, 0, count);
}
bo.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
The content-length is a header set by the server. I would check to make sure that your server is returning the correct content-length. You can do that with cUrl:
curl -v http://path/to/file.txt
That should show you the headers that were sent and returned.
A quick workaround I can think of, is just ignoring the content-length and reading input stream until there's nothing left to read.
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8192);
int read = inputStream.read();
while (read != -1) {
byteArrayOutputStream.write((byte) read);
read = inputStream.read();
}
byteArrayOutputStream.flush();
buf = byteArrayOutputStream.toByteArray();

Getting SQLiteDatabaseCorruptException

The issue is that I'm getting the SqLiteDatabaseCorrupException while executing the next code:
ArrayList<Advertiser> arr = new ArrayList<Advertiser>();
Cursor holo = db.rawQuery("select * from Advertiser;", null);
while(holo.moveToNext()){
Advertiser adver = new Advertiser();
adver.setId(holo.getString(0));
adver.setNombre(holo.getString(1));
adver.setDescripcion(holo.getString(2));
adver.setDireccion(holo.getString(3));
adver.setContacto(holo.getString(4));
adver.setSitioWeb(holo.getString(5));
adver.setFacebook(holo.getString(6));
adver.setTwitter(holo.getString(7));
adver.setPosx(holo.getDouble(8));
adver.setPosy(holo.getDouble(9));
adver.setCiudad(holo.getString(10));
System.out.println("Objeto: " + adver.toString());
arr.add(adver);
}
What happens is that the while starts executing normally, but it gets to a point when the log cat shows that the database is corrupt and then the database gets eliminated.
Any reasons why this is happening?
EDITED:
I forgot to add, my application downloads the database when the main activity stars, does that has something to do with the database getting corrupted? The database is downloaded and stored on the sd card.
EDITED #2:
Here is the code of how I downloaded the database, hope that this helps:
public void DescargaBD() {
try {
URL url = new URL(
"http://71.6.150.179:8079/dbHandler.axd?SqliteDbVersion=0");
HttpURLConnection urlConnection = (HttpURLConnection) url
.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
urlConnection.connect();
File SDCardRoot = Environment.getExternalStorageDirectory();
File file = new File(SDCardRoot, "DirLaguna.db");
FileOutputStream fileOutput = new FileOutputStream(file);
InputStream inputStream = urlConnection.getInputStream();
int totalSize = urlConnection.getContentLength();
int downloadedSize = 0;
byte[] buffer = new byte[1024];
int bufferLength = 0;
while ((bufferLength = inputStream.read(buffer)) > 0) {
fileOutput.write(buffer, 0, bufferLength);
downloadedSize += bufferLength;
}
fileOutput.close();
location = file.getAbsolutePath();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Thanks in advance.

Problems reading a huge file of 12 MB (java.lang.OutOfMemoryError)

i need to open a file of 12 Megabytes, but actually i'm doing it creating a buffer of 12834566-byte, because the size of the file is 12MB and i am developing this app for Android mobile systems.
Then, i supose i have to read with blocks of 1024 Kbytes instead of one block of 12 Mbytes, with a for, but i don't know how to do it, i need a little help with it.
This is my actual code:
File f = new File(getCacheDir()+"/berlin.mp3");
if (!f.exists()) try {
InputStream is = getAssets().open("berlin.mp3");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
FileOutputStream fos = new FileOutputStream(f);
fos.write(buffer);
fos.close();
} catch (Exception e) { throw new RuntimeException(e); }
Please, can someone tell me what i have to changue in this code to read blocks of 1024 Kbytes instead of one block of 12 Mbytes?
THanks!
Try copying 1 KB at a time.
File f = new File(getCacheDir()+"/berlin.mp3");
if (!f.exists()) try {
byte[] buffer = new byte[1024];
InputStream is = getAssets().open("berlin.mp3");
FileOutputStream fos = new FileOutputStream(f);
int len;
while((len = is.read(buffer)) > 0)
fos.write(buffer, 0, len);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
IOUtils.close(is); // utility to close the stream properly.
IOUtils.close(fos);
}
Does Android support symbolic or hand links like UNIX? If it does, this would be faster/more efficient.
File f = new File(getCacheDir()+"/berlin.mp3");
InputStream is = null;
FileOutputStream fos = null;
if (!f.exists()) try {
is = getAssets().open("berlin.mp3");
fos = new FileOutputStream(f);
byte[] buffer = new byte[1024];
while (is.read(buffer) > 0) {
fos.write(buffer);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// proper stream closing
if (is != null) {
try { is.close(); } catch (Exception ignored) {} finally {
if (fos != null) {
try { fos.close(); } catch (Exception ignored2) {}
}
}
}
}
import org.apache.commons.fileupload.util.Streams;
InputStream in = getAssets().open("berlin.mp3");
OutputStream out = new FileOutputStream(f);
Streams.copy(in, out, true);

Android:"Unexpected end of stream" exception downloading large files

I am building an Android Application and I need to download a file from a url, which is 33 MB large.
Here the download task:
try {
int MAX_BUFFER_SIZE = 4096;
URL mUrl = new URL(params[0]);
HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection();
connection.setRequestMethod("GET");
long length = connection.getContentLength(), downloaded = 0;
int read;
byte [] buffer = new byte[(((int)length) > MAX_BUFFER_SIZE) ? MAX_BUFFER_SIZE : (int)length];
String filename = getFilename(mUrl);
File file = new File (SDCARD_ROOT);
if (!file.exists() || !file.isDirectory()){
file.mkdir();
}
this.filename = filename;
file = new File (SDCARD_ROOT + this.filename);
FileOutputStream fos = new FileOutputStream (file);
//Start downloading
InputStream stream = connection.getInputStream();
while ((read=stream.read(buffer)) > -1){
fos.write(buffer, 0, read);
downloaded += read;
publishProgress((int) ((float) downloaded/length * 100));
}
fos.close();
return 1;
} catch (Exception e){
Log.e("REV-PARTS", "Revolver parts error in DownloadTask: " + e.getMessage());
return 2;
}
It works right with small files (1-15 MB), but it will return a "unexpected end of stream" exception with large files.
Setting a chunk size seemed to work for me.
connection.setChunkedStreamingMode(1048576);
For large files you need to set the connection time out manually by using the following code.
I have set the time out to 3 minutes
connection.setConnectTimeout(180000);
connection.setReadTimeout(180000);
While you catch the exception, I try the method downContinue(). I can show my code:
private void downloadApk() {
thread1 = new Thread() {
public void run() {
File oFile = null;
try {
URL url = new URL(PQGLApplication.resrootURL + "apk/PQGLMap.apk");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
ReadableByteChannel channel =
Channels.newChannel(urlConnection.getInputStream());
oFile =
new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/" + "hy_ht_new/" + "test2" + ".apk");
oFile.setWritable(true);
oFile.setReadable(true);
if (oFile.exists()) {
oFile.delete();
}
FileOutputStream fos = new FileOutputStream(oFile);
fileSize = urlConnection.getContentLength();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int noOfBytes = 0;
byte[] data = null;
sendApkMessage(0, 0);
while ((noOfBytes = channel.read(buffer)) > 0) {
data = new byte[noOfBytes];
System.arraycopy(buffer.array(), 0, data, 0, noOfBytes);
buffer.clear();
fos.write(data, 0, noOfBytes);
downLoadFileSize += noOfBytes;
sendApkMessage(1, downLoadFileSize);
}
fos.flush();
fos.close();
channel.close();
sendApkMessage(2, oFile.getAbsolutePath());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
downContinue();
}
};
};
thread1.start();
}
private void downContinue() {
continueTime++;
try {
if (continueTime == 3) {
continueTime = 0;
sendApkMessage(4, 0);
Log.e("what is the continuetime", "continueTime" + continueTime);
} else {
URL url = new URL(PQGLApplication.resrootURL + "apk/PQGLMap.apk");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
File oFile =
new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/"
+ "hy_ht_new/" + "test2" + ".apk");
RandomAccessFile oSavedFile = new RandomAccessFile(oFile, "rw");
FileOutputStream fos = new FileOutputStream(oFile);
ReadableByteChannel channel = Channels.newChannel(urlConnection.getInputStream());
// oSavedFile.seek(nPos);
ByteBuffer buffer = ByteBuffer.allocate(1024);
byte[] data = null;
int temp = 0;
sendApkMessage(3, oFile.getAbsolutePath());
while ((temp = channel.read(buffer)) > 0) {
data = new byte[temp];
System.arraycopy(buffer.array(), 0, data, 0, temp);
buffer.clear();
fos.write(data, 0, temp);
}
fos.flush();
fos.close();
oSavedFile.close();
sendApkMessage(2, oFile.getAbsolutePath());
continueTime = 0;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e("what is the exception", e.toString() + continueTime);
downContinue();
}
}
This downContinue method is used to solve this problem. At least, the file is downloaded successfully!

Categories