package net.oni2.httpfiledownloader; import java.io.BufferedInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.net.URL; import java.net.URLConnection; import java.util.HashSet; import net.oni2.ProxySettings; /** * @author Christian Illy */ public class FileDownloader implements Runnable { /** * @author Christian Illy */ public enum EState { /** * Downloader initialized but not started */ INIT, /** * Download running */ RUNNING, /** * Download suspended */ PAUSED, /** * Download interrupted */ INTERRUPTED, /** * Download finished successfully */ FINISHED, /** * Aborted because of an error */ ERROR }; private HashSet listeners = new HashSet(); private Thread t = null; private URL url = null; private File target = null; private int fileLength = Integer.MAX_VALUE; private int downloaded = 0; private EState state = EState.INIT; /** * @param url * URL of file to download * @param target * Path of target file to save to * @throws IOException * If url could not be opened */ public FileDownloader(String url, File target) throws IOException { this.url = new URL(url); this.target = target; } /** * @param listener * Listener to add */ public void addListener(FileDownloadListener listener) { listeners.add(listener); } /** * @param listener * Listener to remove */ public void removeListener(FileDownloadListener listener) { listeners.remove(listener); } /** * Start the download process */ public synchronized void start() { if (t == null) { state = EState.RUNNING; t = new Thread(this); t.start(); } } /** * @param suspend * Suspend or resume */ public synchronized void suspend(boolean suspend) { if ((state == EState.RUNNING) || (state == EState.PAUSED)) { if (suspend) state = EState.PAUSED; else state = EState.RUNNING; updateStatus(downloaded, fileLength); } } /** * Stop (abort) download */ public synchronized void stop() { if (state != EState.FINISHED) { state = EState.INTERRUPTED; if (t != null) { try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } t = null; } updateStatus(0, 1); if (target.exists()) target.delete(); } } /** * @return current state */ public EState getState() { return state; } private synchronized void updateStatus(int current, int total) { downloaded = current; for (FileDownloadListener l : listeners) { l.statusUpdate(this, state, current, total); } } @Override public void run() { int downloaded = 0; String strLastModified = null; String strEtag = null; RandomAccessFile outFile = null; try { outFile = new RandomAccessFile(target, "rw"); } catch (FileNotFoundException e1) { e1.printStackTrace(); state = EState.ERROR; updateStatus(downloaded, fileLength); return; } try { while (downloaded < fileLength) { switch (state) { case ERROR: updateStatus(downloaded, fileLength); return; case PAUSED: try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } break; case INTERRUPTED: return; case RUNNING: BufferedInputStream input = null; try { URLConnection connection = url .openConnection(ProxySettings.getInstance() .getProxy()); connection.setRequestProperty("Cache-Control", "no-cache"); if (downloaded == 0) { connection.connect(); strLastModified = connection .getHeaderField("Last-Modified"); strEtag = connection.getHeaderField("ETag"); fileLength = connection.getContentLength(); } else { connection.setRequestProperty("Range", "bytes=" + downloaded + "-"); if (strEtag != null) connection.setRequestProperty("If-Range", strEtag); else connection.setRequestProperty("If-Range", strLastModified); connection.connect(); } // Setup streams and buffers. input = new BufferedInputStream( connection.getInputStream(), 8192); if (downloaded > 0) outFile.seek(downloaded); byte data[] = new byte[1024]; // Download file. int dataRead = 0; int i = 0; while (((dataRead = input.read(data, 0, 1024)) != -1) && (state == EState.RUNNING)) { outFile.write(data, 0, dataRead); downloaded += dataRead; if (downloaded >= fileLength) break; i++; if ((i % 50) == 0) updateStatus(downloaded, fileLength); } input.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); try { if (input != null) input.close(); } catch (IOException e2) { e2.printStackTrace(); } } break; default: break; } } } finally { try { // Close streams. outFile.close(); } catch (IOException e) { e.printStackTrace(); } } state = EState.FINISHED; updateStatus(downloaded, fileLength); } /** * @return the target */ public File getTarget() { return target; } /** * @return the downloaded size */ public int getDownloaded() { return downloaded; } }