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..
Related
this code is giving me some problems. This's simply the thread portion of a Service that receive data sent trough a TCP connection. This data is an image (160x120x16bpp = 38400 bytes) feed to an Activity trough a callback.
public void run() {
InetAddress serverAddr;
link_respawn = 0;
try {
serverAddr = InetAddress.getByName(VIDEO_SERVER_ADDR);
} catch (UnknownHostException e) {
Log.e(getClass().getName(), e.getMessage());
e.printStackTrace();
return;
}
Socket socket = null;
DataInputStream stream;
do {
bad_frames = 0;
frames = 0;
status = FrameDecodingStatus.Idle;
try {
socket = new Socket(serverAddr, VIDEO_SERVER_PORT);
stream = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
final byte[] _data = new byte[PACKET_SIZE];
final byte[] _image_data = new byte[IMAGE_SIZE];
int _data_index = 0;
while (keepRunning) {
if (stream.read(_data, 0, _data.length) == 0)
continue;
for (byte _byte : _data) {
if (status == FrameDecodingStatus.Idle) {
if (_byte == SoF) {
status = FrameDecodingStatus.Data;
_data_index = 0;
}
} else if ((status == FrameDecodingStatus.Data) && (_data_index < IMAGE_SIZE)) {
_image_data[_data_index] = _byte;
_data_index++;
} else if ((status == FrameDecodingStatus.Data) && (_data_index == IMAGE_SIZE)) {
if (_byte == EoF) {
if(frameReadyCallBack!=null)
frameReadyCallBack.frameReady(_image_data);
frames++;
status = FrameDecodingStatus.Idle;
}
}
}
}
link_respawn++;
Thread.sleep(VIDEO_SERVER_RESPAWN);
Log.d(getClass().getName(), "Link respawn: " + link_respawn);
} catch (Throwable e) {
Log.e(getClass().getName(), e.getMessage());
e.printStackTrace();
}
} while (keepRunning);
if (socket != null) {
try {
socket.close();
} catch (Throwable e) {
Log.e(getClass().getName(), e.getMessage());
e.printStackTrace();
}
}
}
the Android Activity that receive the callback find data in array corrupted in a very strange way .. i.e. starting at a certain index data into array is set to 0.
How can I avoid this?
read is not readFully. Three-arg read returns the number of bytes that has been read, which is not necessary the full length of the array supplied.
This codes drops the read return value and process the entire array.
if (stream.read(_data, 0, _data.length) == 0)
continue;
for (byte _byte : _data) {
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.
I'm working on an Android project that requires FTP download to be paused/resumed.
Here is the code I use for FTP connection:
ftpClient.setConnectTimeout(25000);
ftpClient.login("login", "password");
ftpClient.changeWorkingDirectory("/audio");
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
then I start download:
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(localFile));
byte[] bytesArray = new byte[4096];
int bytesRead = -1;
totalRead = 0;
if (localFileSize > 0) {
ftpClient.setRestartOffset(localFileSize);
ftpClient.restart(localFileSize);
}
InputStream inputStream = ftpClient.retrieveFileStream(fileName);
while ((bytesRead = inputStream.read(bytesArray)) != -1) {
totalRead += bytesRead;
outputStream.write(bytesArray, (int) localFileSize, bytesRead);
}
success = ftpClient.completePendingCommand();
and I try to pause using abort like this:
if (ftpClient.abort()) {
//connection aborted!;
}
But it seems that abort doesn't work while there is an active download as mentioned here: https://issues.apache.org/jira/browse/NET-419
Is there any way I can perform pause/resume for FTP downloads in Android?
If you really want to pause the download only (as opposite to abort/reconnect/resume), then all you need to do, is temporarily pause the while loop that writes to the data connection stream.
See How to pause/resume thread in Android?
I used the same AsyncTask for connect, download and abort operations:
protected Void doInBackground(Integer... params) {
int command = params.length > 0 ? params[0] : 0;
switch (command) {
case CONNECT:
connect();
break;
case DOWNLOAD:
download();
break;
case PAUSE:
abortConnection();
break;
}
return null;
}
To make sure there is only one task running each time I used Singleton design pattern
public static FtpConnectTask getInstance(FTPClientWrapper ftpClient) {
if (instance != null) {
if (instance.getStatus().name().toLowerCase().equals("running"){
instance.cancel(true);
}
if (instance.getStatus().name().toLowerCase().equals("finished")) {
// task finished!
}
}
instance = new FtpConnect(uiListener, ftpClientW);
return instance;
}
This is code of download() method:
we look for local file size each time the download starts, then we use this size as starting point
//create local file
File localFile = new File(outDir, FILE_NAME));
// get file size if the file exists
long localFileSize = localFile.length();
//retrieve file from server
try {
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(localFile, true));
byte[] bytesArray = new byte[4096];
int bytesRead;
int totalRead = (int) localFileSize;
ftpClient.restart(localFileSize);
InputStream inputStream = ftpClient.retrieveFileStream(REMOTE_FILE);
while ((bytesRead = inputStream.read(bytesArray)) != -1) {
totalRead += bytesRead;
outputStream.write(bytesArray, 0, bytesRead);
// break the loop when AsyncTask is cancelled
if(isCancelled()){
break;
}
}
if (ftpClient.completePendingCommand()) {
// Task completed!
}
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
This is code of abortConnection()
if (ftpClient.isConnected()) {
try {
if (ftpClient.abort()) {
// Connection aborted!
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
// Not connected!
}
To resume your download just call download() again.
I am trying to playback audio and keep it continuous and free from skips or blank spots. I have to first receive as bytes in chunks and convert this to mp3 to be streamed by the servletOutputStream. I only start playing once enough bytes have been collected by the consumer in an attempt to maintain a constant flow of audio. As you can see I have hard coded this buffer but would like it to work for any size of audio bytes. I was wondering if anyone had come across a similar problem and had any advice?
Thanks in advance. Any help would be greatly appreciated.
public class Consumer extends Thread {
private MonitorClass consBuf;
private InputStream mp3InputStream = null;
private OutputStream OutputStream = null;
public Consumer (MonitorClass buf, OutputStream servlet)
{
consBuf = buf;
OutputStream = servlet;
}
public void run()
{
byte[] data;
byte[] tempbuf;
int byteSize = 60720; //This should be dynamic
int byteIncrement = byteSize;
int dataPlayed = 0;
int start = 0;
int buffer = 0;
boolean delay = true;
AudioFormat generatedTTSAudioFormat = getGeneratedAudioFormat();
try
{
while(true)
{
try
{
data = consBuf.get(); //gets data from producer using a shared monitor class
if(data.length >= byteSize) //Buffer size hit, start playing
{
if(delay) //help with buffering
{
System.out.println("Pre-delay...");
consBuf.preDelay();
delay = false;
}
tempbuf = new byte[byteIncrement];
arraySwap(data, tempbuf, start, byteSize);
System.out.println("Section to play: " + start + ", " + byteSize);
mp3InputStream = FishUtils.convertToMP3( new ByteArrayInputStream(tempbuf), generatedTTSAudioFormat);
copyStream(mp3InputStream, OutputStream);
System.out.println("Data played: " + byteSize);
System.out.println("Data collected: " + consBuf.getDownloadedBytes() );
dataPlayed = byteSize;
start = byteSize;
byteSize += byteIncrement;
}
if( consBuf.getIsComplete() )
{
if (consBuf.checkAllPlayed(dataPlayed) > 0)
{
System.out.println("Producer finished, play remaining section...");
//mp3InputStream = convertToMP3(new ByteArrayInputStream(tempbuf), generatedTTSAudioFormat);
//copyStream(mp3InputStream, OutputStream);
}
System.out.println("Complete!");
break;
}
}
catch (Exception e)
{
System.out.println(e);
return;
}
}
}
finally
{
if (null != mp3InputStream)
{
try
{
mp3InputStream.skip(Long.MAX_VALUE);
}
catch (Exception e)
{}
}
closeStream(mp3InputStream);
closeStream(OutputStream);
}
}
}
Can anybody see what is wrong with this code. it does not show up progress-bar but uploades all the files.
I did checkout sun tutorial and swingworkers also but i couldn't fix it yet.
private static boolean putFile(String m_sLocalFile, FtpClient m_client) {
boolean success = false;
int BUFFER_SIZE = 10240;
if (m_sLocalFile.length() == 0) {
System.out.println("Please enter file name");
}
byte[] buffer = new byte[BUFFER_SIZE];
try {
File f = new File(m_sLocalFile);
int size = (int) f.length();
System.out.println("File " + m_sLocalFile + ": " + size + " bytes");
System.out.println(size);
FileInputStream in = new FileInputStream(m_sLocalFile);
//test
InputStream inputStream = new BufferedInputStream(
new ProgressMonitorInputStream(null,"Uploading " + f.getName(),in));
//test
OutputStream out = m_client.put(f.getName());
int counter = 0;
while (true) {
int bytes = inputStream.read(buffer); //in
if (bytes < 0)
break;
out.write(buffer, 0, bytes);
counter += bytes;
System.out.println(counter);
}
out.close();
in.close();
inputStream.close();
success =true;
} catch (Exception ex) {
System.out.println("Error: " + ex.toString());
}
return true;
}
I think your code is fine.
Maybe the task isn't taking long enough for the progress bar to be needed?
Here's a modified version of your code which reads from a local file and writes to another local file.
I have also added a delay to the write so that it gives the progress bar time to kick in.
This works fine on my system with a sample 12MB PDF file, and shows the progress bar.
If you have a smaller file then just increase the sleep from 5 milliseconds to 100 or something - you would need to experiment.
And I didn't even know that the ProgressMonitorInputStream class existed, so I've learnt something myself ;].
/**
* main
*/
public static void main(String[] args) {
try {
System.out.println("start");
final String inf = "d:/testfile.pdf";
final String outf = "d:/testfile.tmp.pdf";
final FileOutputStream out = new FileOutputStream(outf) {
#Override
public void write(byte[] b, int off, int len) throws IOException {
super.write(b, off, len);
try {
// We delay the write by a few millis to give the progress bar time to kick in
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
putFile(inf, out);
System.out.println("end");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private static boolean putFile(String m_sLocalFile, OutputStream out /*FtpClient m_client*/) {
boolean success = false;
int BUFFER_SIZE = 10240;
if (m_sLocalFile.length() == 0) {
System.out.println("Please enter file name");
}
byte[] buffer = new byte[BUFFER_SIZE];
try {
File f = new File(m_sLocalFile);
int size = (int) f.length();
System.out.println("File " + m_sLocalFile + ": " + size + " bytes");
System.out.println(size);
FileInputStream in = new FileInputStream(m_sLocalFile);
//test
InputStream inputStream = new BufferedInputStream(
new ProgressMonitorInputStream(null,"Uploading " + f.getName(),in));
//test
//OutputStream out = m_client.put(f.getName());
int counter = 0;
while (true) {
int bytes = inputStream.read(buffer); //in
if (bytes < 0)
break;
out.write(buffer, 0, bytes);
counter += bytes;
System.out.println(counter);
}
out.close();
in.close();
inputStream.close();
success =true;
} catch (Exception ex) {
System.out.println("Error: " + ex.toString());
}
return true;
}