Monday, November 7, 2016

Create a File Downloader with Progress status in Java

Overview:
1) It is very easy to download files with Java using the URL class.
2) Download files gets important while creating Automation Job. For eg. if you need to download the daily builds and then perform your test over them then you can use this.

Where to Download:
Please download the full code from: https://github.com/csanuragjain/filedownloader & then run FileDownloader.java

Program Explanation:

Main method:
      static int downloadinProgress = 0;  
      static File f = new File("");  
      public static void main(String[] args) throws MalformedURLException,  
                IOException {  
           Scanner s = new Scanner(System.in);  
           String ch = "YES";  
           while (!ch.equals("NO")) {  
                System.out.println("Enter the url to download from");  
                final String downloadURL = s.nextLine();  
                new Thread() {  
                     public void run() {  
                          new FileDownloader().downloadFile(downloadURL);  
                     }  
                }.start();  
                System.out.println("Do you wish to download more files (YES/NO)");  
                ch = s.nextLine();  
           }  
      }  

How it works:

1) We created 2 static variables, downloadinProgress - tells how many active downloads are their & f - Used later to get the current File path where downloaded files are kept
2) Scanner s is used for taking user input
3) We ask user for the download url and pass the same as argument inside downloadFile function
4) downloadFile function is called within anonymous thread so that we can have multiple file downloads
5) Step 3 keeps on repeating until user press NO

downloadFile method:
      public void downloadFile(String downloadURL) {  
           downloadinProgress++;  
           System.out.println("**Download stats: Number of ongoing downloads: "  
                     + downloadinProgress);  
           BufferedInputStream in = null;  
           RandomAccessFile fout = null;  
           String fileName = "";  
           long fileSize = 0;  
           long downloaded = 0;  
           try {  
                URL u = new URL(downloadURL);  
                URLConnection uc = u.openConnection();  
                uc.setRequestProperty("Range", "bytes=0-");  
                fileName = u.getFile();  
                fileName = fileName.substring(fileName.lastIndexOf('/') + 1);  
                fileSize = uc.getContentLength();  
                System.out.println("**Download stats: Downloading " + fileName  
                          + " which has size of " + (fileSize / 1000) + "KB");  
                in = new BufferedInputStream(uc.getInputStream());  
                fout = new RandomAccessFile(fileName, "rw");  
                fout.seek(downloaded);  
                final byte data[] = new byte[1024];  
                int count;  
                boolean ALERT_WHEN_50_PERCENT_COMPLETE = true;  
                while ((count = in.read(data, 0, 1024)) != -1) {  
                     fout.write(data, 0, count);  
                     downloaded += count;  
                     if (ALERT_WHEN_50_PERCENT_COMPLETE  
                               && downloaded * 2 >= fileSize) {  
                          System.out.println("**Download stats: " + fileName  
                                    + ": Completed 50% download...");  
                          ALERT_WHEN_50_PERCENT_COMPLETE = false;  
                     }  
                }  
                System.out  
                          .println("**Download stats: Download completed. File at: "  
                                    + f.getAbsolutePath() + File.separator + fileName);  
                downloadinProgress--;  
           } catch (Exception e) {  
                System.out.println("**Download stats: Download Failed for "  
                          + fileName);  
                downloadinProgress--;  
           } finally {  
                if (in != null) {  
                     try {  
                          in.close();  
                     } catch (IOException e) {  
                          // TODO Auto-generated catch block  
                          e.printStackTrace();  
                     }  
                }  
                if (fout != null) {  
                     try {  
                          fout.close();  
                     } catch (IOException e) {  
                          // TODO Auto-generated catch block  
                          e.printStackTrace();  
                     }  
                }  
           }  
      }  

How it works:

1) Increment downloadinProgress since a new download has begun
2) Now we open a connection to the download url
3) We pass range as bytes=0- which tells that i am interested in downloading the content from byte 0 to last byte. This was not neccessary here but it is very useful when you are creating a downloader with resume capacity. So assume if download failed at 100 byte then you can restart your download with range as 100-
4) Now we extract the filename using getFile and filesize using getContentLength
5) We define a RandomAccessFile object called fout and point this to the last byte on the file where we are going to write the downloaded content. In our case it will point to starting of file since our download file did not exist. RandomAccessFile is useful when you want to resume a download and want to write in file from the point where download failed.
6) Now we get the input stream from url connection and start writing the content to the resulting file at 1024 bytes per iteration
7) We kept a tracker ALERT_WHEN_50_PERCENT_COMPLETE which will notify when 50% of file download is complete using the downloaded variable which tracks the number of bytes written to file. You can use the same trick to update a progress bar when using swing
8) After this completes, objects are closed
9) File download is complete

Hope it helps :)

1 comment: