Search code examples
javaandroiddownloadftpapache-commons-net

Android FTP download pause/resume


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?


Solution

  • 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.