package nl.helixsoft.gui; import java.awt.GraphicsEnvironment; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.net.URLConnection; import javax.swing.ProgressMonitorInputStream; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPReply; import nl.helixsoft.util.HFileUtils; public class DownloadUtils { public static void downloadFile(URL url, File dest) throws IOException { downloadFile(url, dest, false); } //TODO: add option for silent downloading, or for a CLI progress monitor. public static void downloadFile(URL url, File dest, boolean overwrite) throws IOException { // use apache client for FTP to work around 2G size limit in java. // https://community.oracle.com/thread/1144802?start=0&tstart=0 if ("ftp".equals (url.getProtocol())) { downloadFtp (url, dest, overwrite); } else { URLConnection conn = url.openConnection(); downloadFile (conn, dest, overwrite); } } public static void downloadFtp(URL url, File dest) throws IOException { downloadFtp(url, dest, false); } public static void downloadFtp(URL url, File dest, boolean overwrite) throws IOException { FTPClient client = new FTPClient(); client.connect(url.getHost()); String password = System.getProperty("user.name")+ "@" + HFileUtils.safeMachineName("unknown"); client.login("anonymous", password); int reply = client.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { client.disconnect(); throw new IOException ("FTP server refused connection with code: " + reply); } // fos = new FileOutputStream(dest); //TODO - how to determine correct file type? client.setFileType(FTPClient.BINARY_FILE_TYPE); // passive mode is more robust // see http://www.jscape.com/blog/bid/80512/Active-v-s-Passive-FTP-Simplified // TODO - according to some, both active and passive should be tried in turns... client.enterLocalPassiveMode(); FTPFile[] stat = client.listFiles(url.getPath()); long size; if (stat.length != 1) { client.disconnect(); System.err.println ("could not stat " + url + "\nlength: " + stat.length + "\nreply: " + client.getReplyString()); throw new IOException ("Could not stat " + url); } else { size = stat[0].getSize(); } InputStream inputStream = client.retrieveFileStream(url.getPath()); downloadFile (inputStream, dest, url, size, overwrite); // // client.retrieveFile("/" + url.getPath(), fos); // fos.close(); client.disconnect(); } public static void downloadStream (URLConnection conn, OutputStream out) throws IOException { downloadStream (conn.getInputStream(), out, conn.getURL(), conn.getContentLength() /* TODO from java 1.7: getContentLengthLong() */ ); } //TODO: add option for silent downloading, or for a CLI progress monitor. /** * Download from URLconnection to an output stream. * Show progress dialog. * @param out * @throws IOException */ public static void downloadStream (InputStream in, OutputStream out, URL url, long contentLength) throws IOException { System.out.println ("Please wait while downloading file from " + url); InputStream xin; if (!GraphicsEnvironment.isHeadless()) { ProgressMonitorInputStream pin = new ProgressMonitorInputStream( null, "Downloading " + url, in ); pin.getProgressMonitor().setMillisToDecideToPopup(0); pin.getProgressMonitor().setMaximum((int)contentLength); //TODO - potential for int overflow xin = pin; } else { //TODO: use printProgBar as alternative xin = in; } byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = xin.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } in.close(); out.flush(); } public static void downloadFile(URLConnection conn, File dest, boolean overwrite) throws IOException { downloadFile (conn.getInputStream(), dest, conn.getURL(), conn.getContentLength() /* from java 1.7: getContentLengthLong() */, overwrite ); } public static void downloadFile(URLConnection conn, File dest) throws IOException { downloadFile(conn, dest, false); } /** * Download from URLConnection into a File. * If the destination file already exists, an exception is thrown. * This function uses a temporary intermediate file, so if there is an error during download, no file is created. * * The URLConnection form allows setting extra request headers, such as for HTTP Basic Authentication etc. */ public static void downloadFile(InputStream in, File dest, URL url, long contentLength) throws IOException { downloadFile(in, dest, url, contentLength, false); } //TODO: add option for silent downloading, or for a CLI progress monitor. /** * Download from URLConnection into a File. * This function uses a temporary intermediate file, so if there is an error during download, no file is created. * * The URLConnection form allows setting extra request headers, such as for HTTP Basic Authentication etc. * * @param overwrite if false, will throw an exception if destination file exists. If true, will delete destination file if it exists. */ public static void downloadFile(InputStream in, File dest, URL url, long contentLength, boolean overwrite) throws IOException { if (!overwrite && dest.exists()) throw new IOException ("File " + dest + " already exists"); File temp = File.createTempFile(dest.getName(), ".tmp", dest.getParentFile()); try { OutputStream out = new BufferedOutputStream( new FileOutputStream(temp)); downloadStream (in, out, url, contentLength); out.close(); if (dest.exists()) { if (!dest.delete()) throw new IOException ("Could not remove existing version of " + dest); } if (!temp.renameTo(dest)) { throw new IOException ("Could not rename " + temp + " to " + dest); } } finally { temp.delete(); } } public static boolean isHeadLess() { return GraphicsEnvironment.isHeadless(); } public static void printProgBar(int percent){ StringBuilder bar = new StringBuilder("["); for(int i = 0; i < 50; i++){ if( i < (percent/2)){ bar.append("="); }else if( i == (percent/2)){ bar.append(">"); }else{ bar.append(" "); } } bar.append("] " + percent + "% "); System.out.print("\r" + bar.toString()); } }