I have an asynchronous method that allows downloading files.
If the middle of the download, I will remove the connection (wifi or 3g) timeout never occurs.
Always stay in the next loop waiting to return the connection:
while ((count = input.read(data)) != -1) {
System.out.println("state 5");
total += count;
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
I do:
private class DownloaderFile extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... params) {
...
try{
URLConnection connection = urlFinal.openConnection();
connection.setConnectTimeout(TIMEOUT_VALUE);
connection.setReadTimeout(TIMEOUT_VALUE);
connection.connect();
int fileLength = connection.getContentLength();
InputStream input = new BufferedInputStream(urlFinal.openStream());
OutputStream output = new FileOutputStream(folder + params[0]);
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
//always wait here
System.out.println("state 5");
total += count;
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (SocketTimeoutException e) {
System.out.println("TIMEOUT!!! " + TIMEOUT_VALUE + " elapsed.");
callback.onDownloadEnd(DOWNLOAD_ERROR);
}
...
}
...
This is not a great solution but it works. While I think of another solution...
while ((count = input.read(data)) != -1) {
if (!isInternet(context)){
callback.onDownloadEnd(DOWNLOAD_ERROR);
return "error";
}
total += count;
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
I suspect SocketTimeoutException being the wrong exception to look for as the connection is correcty established in your test, what if you change it to Exception? Just to see if that helps.
I can see from: How to set HttpResponse timeout for Android in Java that i was mistaken.
From the information I linked I found that you probably need to set:
// Set the default socket timeout (SO_TIMEOUT)
// in milliseconds which is the timeout for waiting for data.
int timeoutSocket = 5000;
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
Whatever the cause may be I'm guessing when 3g/wifi is no longer available the thread that reads from socket is blocked.
One approach you can take here is to perform the socket read on a separate thread, and use Thread.join(long millis) method to wait up to maximum milliseconds for it to complete.
Thread t = new Thread(new Runnable() {
void run() {
...
while ((count = input.read(data)) != -1) {
...
}
...
}
}).start();
t.join(TIMEOUT_VALUE); // will wait here until either the thread t is done or times out
Related
I'm trying to download files in java in a multi-segment way (i.e., dividing it to several parts and downloading each part in a separate thread parallelly) but when I use the code below, it seems each thread is downloading the whole file instead of just a part of it but when it finishes, file is downloaded correctly.
note that "downloadedSizeCombined" is sum of all bytes which are downloaded by all the threads and ArrayList "downloadedSize" keeps track of bytes which are downloaded by a single thread.
this method is in class Download which extends SwingWorker.
public Void doInBackground() {
ExecutorService es = Executors.newCachedThreadPool();
for (int i = 0; i < MAX_NUMBER_OF_PARTS; i++) {
int numOfThePart = i;
es.execute(new Runnable() {
#Override
public void run() {
RandomAccessFile file = null;
InputStream stream = null;
try {
while (Download.this.getStatus() == WAITINGLIST) {
Thread.sleep(1);
}
// Open connection to URL.
HttpURLConnection connection =
(HttpURLConnection) url.openConnection();
// Specify what portion of file to download.
int startByte = numOfThePart * sizeOfFile / MAX_NUMBER_OF_PARTS;
int endByte = ((numOfThePart + 1) * sizeOfFile / MAX_NUMBER_OF_PARTS) - 1;
if (numOfThePart == MAX_NUMBER_OF_PARTS)
endByte = ((numOfThePart + 1) * sizeOfFile / MAX_NUMBER_OF_PARTS);
connection.setRequestProperty("Range",
"bytes=" + ((startByte + downloadedSize.get(numOfThePart))) + "-" + endByte);
// Connect to server.
connection.connect();
// Check for valid content length.
int contentLength = connection.getContentLength();
if (contentLength < 1) {
System.out.println("1");
}
/* Set the size for this download if it
hasn't been already set. */
if (sizeOfFile == -1) {
sizeOfFile = contentLength;
}
file = new RandomAccessFile(new File(s.getCurrentDirectory(), getFileName(url)),
"rw");
file.seek(startByte + downloadedSize.get(numOfThePart));
fileLocation = new File(s.getCurrentDirectory(), getFileName(url));
stream = connection.getInputStream();
while (status == CURRENT) {
file.seek(startByte + downloadedSize.get(numOfThePart));
byte buffer[];
buffer = new byte[MAX_BUFFER_SIZE];
// Read from server into buffer.
int read = stream.read(buffer);
if (read == -1)
break;
// Write buffer to file.
file.write(buffer, 0, read);
downloadedSizeCombined += read;
downloadedSize.set(numOfThePart, downloadedSize.get(numOfThePart) + read);
publish(numOfThePart);
while (status == PAUSED) {
Thread.sleep(1);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// Close file.
if (file != null) {
try {
file.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// Close connection to server.
if (stream != null) {
try {
stream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
});
}
return null;
}
Thanks in advance.
Can't we use UDP connection? So if we use DatagramSocket class, it will anyways send the data in packets. Try this.
Will get back on this soon..
I'm making an app with a download process, it will download a certain file. If I turn off the Wi-Fi when the file is downloading, the app crashes.
This is the log: recvfrom failed: ETIMEDOUT (Connection timed out)
I have a conditional, but it seems not to work. If I debug the code, it seems to enter the conditional.
else {
Thread.sleep(4000); //doesn't work, doesn't sleep
downloadresult = false;
}
I want the download process to pause when I turn off the Wi-Fi. Is there any way of doing this?
Thanks in advance.
All code
protected String doInBackground(String... f_url) {
try {
long total = 0;
URL url = new URL(f_url[0]);
HttpURLConnection conection = (HttpURLConnection) url.openConnection();
int lenghtOfFile = conection.getContentLength();
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file));
conection.connect();
BufferedInputStream input = new BufferedInputStream(conection.getInputStream());
byte data[] = new byte[8192];
int lastcount = 0;
while ((count = input.read(data)) != -1) {
if (isCanceled) { // this code waiting the click button :)
file.delete();
downloadresult = false;
break;
}
if (intCheck()) { // check internet and download
total += count;
downloadresult = true;
int ProgBarCount = (int) ((total * 100) / lenghtOfFile);
if (ProgBarCount > lastcount) {
lastcount = ProgBarCount;
publishProgress(Integer.toString(ProgBarCount));
}
output.write(data, 0, count);
}
else {
Thread.sleep(4000); //doesn't work, doesn't sleep
downloadresult = false;
}
}
output.flush();
output.close();
input.close();
}
catch (Exception e) {
e.printStackTrace();
exmessage = e.getMessage().toString();
downloadresult = false;
}
return null;
}
If I debug the code, it works perfectly. If the app can't dowload the file, I want the app to wait 4000ms and then try again, but if I run the app, it crashes.
How can i pause/resume the download process. Thank You
I fixed the problem :)
Thanks for all respond, I love it <3
Code:
protected String doInBackground(String... f_url) {
try {
long total = 0;
URL url = new URL(f_url[0]);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
/* if (file.exists())
{
connection.setAllowUserInteraction(true);
connection.setRequestProperty("Range", "bytes=" + lenghtOfFile + "-");
}*/
if(file.exists()){
deneme = file.length();
connection.setRequestProperty("Range", "bytes="+(file.length())+"-");
}
else{
connection.setRequestProperty("Range", "bytes=" + deneme + "-");
}
String connectionField = connection.getHeaderField("content-range");
if (connectionField != null)
{
String[] connectionRanges = connectionField.substring("bytes=".length()).split("-");
deneme = Long.valueOf(connectionRanges[0]);
}
if (connectionField == null && file.exists())
file.delete();
connection.setConnectTimeout(14000);
connection.setReadTimeout(20000);
connection.connect();
long lenghtOfFile = connection.getContentLength() + deneme;
RandomAccessFile output = new RandomAccessFile(file,"rw");
BufferedInputStream input = new BufferedInputStream(connection.getInputStream());
output.seek(deneme);
byte data[] = new byte[1024];
int lastcount = 0;
while ((count = input.read(data,0,1024)) != -1) {
if (isCanceled) { // this code waiting the click button :)
file.delete();
downloadresult = false;
break;
}
if (intCheck()) { // check internet and download
total += count;
downloadresult = true;
int ProgBarCount = (int) ((total * 100) / lenghtOfFile);
if (ProgBarCount > lastcount) {
lastcount = ProgBarCount;
publishProgress(Integer.toString(ProgBarCount));
}
output.write(data, 0, count);
}
}
// output.flush();
output.close();
input.close();
}
catch (Exception e) {
e.printStackTrace();
exmessage = e.getMessage().toString();
downloadresult = false;
}
return null;
}
Don't pause background tasks. When anything goes wrong cancel the task. Remember your app was smart enough to start a background task. It will be smart enough to restart it again later. If your going to pause a background task it should be paused only at the users bequest.
Hello My Respected Seniors :)
My Goal: Download a URL Resource, given a URL, by using Multi-Threading in Java, i.e. download a single file into multiple pieces (much like how IDM does) & at the end of download, combine all of them to 1 final file.
Technology Using: Java, RandomAccessFile, MultiThreading, InputStreams
Problem:
The file is downloaded fine with exact KB size, I've checked many times, but the final file is corrupted. For example, If I download an Image, it will be somewhat blurry, If I download an .exe, it downloads fine but when I run the .exe file, it says "media is damaged, retry download".
This is my Main code from which I call to thread class with parameters such as fileName, starting Range and ending Range for a connection as well as a JProgressBar for every thread which will update its own respectively.
public void InitiateDownload()
{
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
uc.connect();
long fileSize = uc.getContentLengthLong();
System.out.println("File Size = "+ fileSize );
uc.disconnect();
chunkSize = (long) Math.ceil(fileSize/6);
startFrom = 0;
endRange = (startFrom + chunkSize) - 1;
Thread t1 = new MyThread(url, fileName, startFrom, endRange, progressBar_1);
t1.start();
//-----------------------------------------
startFrom += chunkSize;
endRange = endRange + chunkSize;
System.out.println("Part 2 :: Start = " + startFrom + "\tEnd To = " + endRange );
Thread t2 = new MyThread(url, fileName, startFrom, endRange, progressBar_2);
t2.start();
//-----------------------------------------
//..
//..
//..
//-----------------------------------------
startFrom += chunkSize;
long temp = endRange + chunkSize;
endRange = temp + (fileSize - temp); //add any remaining bits, that were rounded off in division
Thread t6 = new MyThread(url, fileName, startFrom, endRange, progressBar_6);
t6.start();
//-----------------------------------------
}
Here is run() function of MyThread class:
public void run() {
Thread.currentThread().setPriority(MAX_PRIORITY);
System.setProperty("http.proxyHost", "192.168.10.50");
System.setProperty("http.proxyPort", "8080");
HttpURLConnection uc = null;
try {
uc = (HttpURLConnection) url.openConnection();
uc.setRequestProperty("Range", "bytes="+startFrom+"-"+range);
uc.connect();
fileSize = uc.getContentLengthLong();
inStream = uc.getInputStream();
int[] buffer = new int[ (int) totalDownloadSize ];
file.seek(startFrom); //adjusted start of file
THIS IS WHERE I THINK THE PROBLEM IS,
run() continued...
for(int i = 0 ; i < totalDownloadSize; i++)
{
buffer[i] = inStream.read();
file.write(buffer[i]);
//Updating Progress bars
totalDownloaded = totalDownloaded + 1;
int downloaded = (int) (100 * ( totalDownloaded/ (float) totalDownloadSize)) ;
progressbar.setValue( downloaded );
}
System.err.println( Thread.currentThread().getName() + "'s download is Finished!");
uc.disconnect();
}
catch(IOException e) {
System.err.println("Exception in " + Thread.currentThread().getName() + "\t Exception = " + e );
}
finally {
try {
file.close();
if(inStream!=null)
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Now the file is downloaded with complete size, but as I said, a little part of it is corrupt.
Now,
If I replace the for loop with following while loop, the problem is completely solved.
int bytesRead = 0;
byte[] buffer = new byte[ (int) totalDownloadSize ];
file.seek(startFrom); //adjusted start of file
while( (bytesRead = inStream.read(buffer) ) != -1 ) {
file.write(buffer, 0, bytesRead);
}
BUT I NEED for LOOP TO MEASURE HOW MUCH FILE EACH THREAD HAS DOWNLOADED & I WANT TO UPGRADE RESPECTIVE JPROGRESSBARs of THREADS.
Kindly help me out with the for loop logic.
OR
If you can advise on how can I upgrade Jprogressbars within while loop. I can't seem to find a way to quantify how much file a thread has downloaded...
I've spent alot of hours & I'm extremely tired now...
You can use the while loop that works, and then keep track of the total amount of bytes read like this:
int totalRead = 0;
while ((bytesRead = inStream.read(buffer)) != -1) {
totalRead += bytesRead;
file.write(buffer, 0, bytesRead);
progressBar.setValue((int)(totalRead / (double) totalDownloadSize));
}
just remember that for (a; b; c) { ... } is equal to a; while (b) { c; ... }.
I have an async downloader in my app, but sometimes the connection is lost, especially when I'm on a mobile connection and if the file is a large one (>10 MB).
Is there a way to catch when a download stops and then force it to resume with the result of completing the download?
This is the async task doInBackground:
protected String doInBackground(String... aurl) {
int count;
try {
URL url = new URL(aurl[0]);
URLConnection conexion = url.openConnection();
// conexion.setRequestProperty("Range", "bytes=" + downloaded + "-");
conexion.connect();
int lenghtOfFile = conexion.getContentLength();
pesoVideo = lenghtOfFile / 1048576;
output = "/sdcard/" + folderString + "/" + nomeFile + ".mp3";
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(
VideoDownloaderBrowserActivity.this.output);
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
publishProgress("" + (int) ((total * 100) / lenghtOfFile));
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (Exception e) {
}
return null;
}
This is the onProgressUpdate:
protected void onProgressUpdate(String... progress) {
if (Integer.parseInt(progress[0]) > progresso) {
...
}
}
Here is a thread discussing resumable downloading in Android below API 9. Otherwise DownloadManager is a good option too for newer versions.
Basically you need to enable byte serving on your server to allow for the resumable downloading.
I'm working on downloading a file on a software, this is what i got, it sucesfully download, and also i can get progress, but still 1 thing left that I dont know how to do. Measure download speed. I would appreciate your help. Thanks.
This is the current download method code
public void run()
{
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try
{
URL url1 = new URL(url);
out = new BufferedOutputStream(
new FileOutputStream(sysDir+"\\"+where));
conn = url1.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
double progress1;
while ((numRead = in.read(buffer)) != -1)
{
out.write(buffer, 0, numRead);
numWritten += numRead;
this.speed= (int) (((double)
buffer.length)/8);
progress1 = (double) numWritten;
this.progress=(int) progress1;
}
}
catch (Exception ex)
{
echo("Unknown Error: " + ex);
}
finally
{
try
{
if (in != null)
{
in.close();
}
if (out != null)
{
out.close();
}
}
catch (IOException ex)
{
echo("Unknown Error: " + ex);
}
}
}
The same way you would measure anything.
System.nanoTime() returns a Long you can use to measure how long something takes:
Long start = System.nanoTime();
// do your read
Long end = System.nanoTime();
Now you have the number of nanoseconds it took to read X bytes. Do the math and you have your download rate.
More than likely you're looking for bytes per second. Keep track of the total number of bytes you've read, checking to see if one second has elapsed. Once one second has gone by figure out the rate based on how many bytes you've read in that amount of time. Reset the total, repeat.
here is my implementation
while (mStatus == DownloadStatus.DOWNLOADING) {
/*
* Size buffer according to how much of the file is left to
* download.
*/
byte buffer[];
// handled resume case.
if ((mSize < mDownloaded ? mSize : mSize - mDownloaded <= 0 ? mSize : mSize - mDownloaded) > MAX_BUFFER_SIZE) {
buffer = new byte[MAX_BUFFER_SIZE];
} else {
buffer = new byte[(int) (mSize - mDownloaded)];
}
// Read from server into buffer.
int read = stream.read(buffer);
if (read == -1)
break;// EOF, break while loop
// Write buffer to file.
file.write(buffer, 0, read);
mDownloaded += read;
double speedInKBps = 0.0D;
try {
long timeInSecs = (System.currentTimeMillis() - startTime) / 1000; //converting millis to seconds as 1000m in 1 second
speedInKBps = (mDownloaded / timeInSecs) / 1024D;
} catch (ArithmeticException ae) {
}
this.mListener.publishProgress(this.getProgress(), this.getTotalSize(), speedInKBps);
}
I can give you a general idea. Start a timer at the beginning of the download. Now, multiply the (percentage downloaded) by the download size, and divide it by the time elapsed. That gives you average download time. Hope I get you on the right track!
You can use System.nanoTime(); as suggested by Brian.
Put long startTime = System.nanoTime(); outside your while loop. and
long estimatedTime = System.nanoTime() - startTime; will give you the elapsed time within your loop.