import java.net.*; import java.io.*; import java.util.*; import java.applet.*; //-- //-- append(StringBuffer) is java 1.4 //-- URL.getPath is java 1.3 //-- working on java 1.1 virtual machine //-------------------------------------------- /** * This class represents a download of a web file. * This class will store data about the process of attempting * to download the resource from the internet. The class * will also be responsable for actually initiating the download * process by opening a connection to the resource etc. * * This was adapted from the SoundDownload class * @see "SoundDownload" * @author matth3wbishopyahoocom */ public class WebDownload extends Object implements Task { //-------------------------------------------- /** the resource to get */ private String url; //-------------------------------------------- /** The time at which the attempted download was begun */ private Date startTime; //-------------------------------------------- /** The time at which the attempted download was terminated * whether successfully or unsuccessfully */ private Date finishTime; //-------------------------------------------- /** A place to store the downloaded resource, */ private String fileName; //-------------------------------------------- /** A directory in which to put files */ private String directoryName = ""; //-------------------------------------------- /** whether to overwrite existing files */ private boolean overwriteFiles = false; //-------------------------------------------- /** whether to reget files that already exist */ private boolean regetExisting; //-------------------------------------------- /** will store error messages produced when attempting to download * the file, such as those produced by the Url class in * attempting to make a connection to the resource. */ private StringBuffer errorMessage; //-------------------------------------------- /** The protocol of the url */ private String protocol; //-------------------------------------------- private int httpResponseCode; //-------------------------------------------- private String httpResponseMessage; //-------------------------------------------- private String httpContentType; //-------------------------------------------- private int httpContentLength; //-------------------------------------------- private String httpContentEncoding; //-------------------------------------------- /** The number of bytes already read from the resource */ private long bytesRead; //-------------------------------------------- /** a chunk for reading from the target, in bytes */ private static int CHUNK_SIZE = 10000; //-------------------------------------------- /** a default file name for downloaded resource */ private String DEFAULT_FILE_NAME = "index"; //-------------------------------------------- /** the progress marker */ private String PROGRESS_MARKER = "."; //-------------------------------------------- /** the reason the download failed */ private String failureCause; //-------------------------------------------- private boolean wasSuccessful; //-------------------------------------------- /** whether the download has started */ private boolean hasStarted; //-------------------------------------------- private boolean hasFinished; //-------------------------------------------- private boolean showProgress = true; //-------------------------------------------- public static String NEWLINE = System.getProperty("line.separator"); //-------------------------------------------- //-- CONSTRUCTORS public WebDownload() { this.initialize(); } //-- constr: () //-------------------------------------------- public WebDownload(String sUrl) { this.initialize(); this.setUrl(sUrl); } //-- METHODS //-------------------------------------------- /* */ public void initialize() { this.overwriteFiles = false; this.regetExisting = true; this.showProgress = true; this.directoryName = ""; this.reset(); } //-------------------------------------------- /** this method sets most of the internal fields to * nothing. this method is mainly to allow one object * to be reused for several downloads. */ public void reset() { this.protocol = ""; this.httpContentType = ""; this.httpContentLength = 0; this.httpContentEncoding = ""; this.httpResponseCode = 0; this.httpResponseMessage = ""; this.bytesRead = 0; this.fileName = ""; this.failureCause = ""; this.errorMessage = new StringBuffer(""); this.hasStarted = false; this.wasSuccessful = false; this.hasFinished = false; this.url = new String(""); this.startTime = new Date(0); this.finishTime = new Date(0); //-- show progress should not be reset //-- the directory name should not be reset //-- regetExisting should not be reset //-- overwriteFiles should not be reset; } //-- method: reset //-------------------------------------------- /* return the target to download */ public String getUrl() { return this.url; } //-------------------------------------------- public void setUrl(String sUrl) { this.reset(); if (sUrl.toLowerCase().startsWith("www.")) { this.url = "http://" + sUrl; } else { this.url = sUrl; } } //-------------------------------------------- public void setLocalFileName(String sLocalFileName) { this.fileName = sLocalFileName; } //-------------------------------------------- /** tries to set the local directory. If a bad string is * given it returns false */ public boolean setLocalDirectory(String sLocalDirectoryName) { File fDirectory = new File(sLocalDirectoryName); if (!fDirectory.exists()) { return false; } if (!fDirectory.isDirectory()) { return false; } //-- remove a final path separator from the name if (sLocalDirectoryName.endsWith(File.separator)) { this.directoryName = sLocalDirectoryName.substring(0, sLocalDirectoryName.length() - 1); } else { this.directoryName = sLocalDirectoryName; } return true; } //-- //-------------------------------------------- /* advises whether the attempted download was carried out successfully */ public boolean wasSuccessful() { return this.wasSuccessful; } //-------------------------------------------- /** sets whether existing files are redownloaded */ public void regetExisting() { this.regetExisting = true; } //-------------------------------------------- /** sets whether existing files are redownloaded */ public void regetExisting(boolean bReget) { this.regetExisting = bReget; } //-------------------------------------------- /** sets whether existing files are overwritten */ public void overwrite() { this.overwriteFiles = true; } //-------------------------------------------- /** sets whether existing files are overwritten */ public void overwrite(boolean bOverwrite) { this.overwriteFiles = bOverwrite; } //-------------------------------------------- /** whether some indicator is printed to the console */ public void showProgress() { this.showProgress = true; } //-------------------------------------------- public void showProgress(boolean bShow) { this.showProgress = bShow; } //-------------------------------------------- public void setProgressMarker(String sMarker) { this.PROGRESS_MARKER = sMarker; } //-------------------------------------------- /* informs of the average download speed */ public float getAverageSpeed() { float fAverageSpeed = 0; if (!this.wasSuccessful) { return 0; } //-- fAverageSpeed = (float)this.bytesRead/this.getDownloadDuration(); return fAverageSpeed; } //-------------------------------------------- /** an alias to comply with the task interface */ public void doTask() { this.download(); } //-------------------------------------------- /** attempt the download and fail with dignity and grace */ public void download() { //-- URL uWebResource; URLConnection urlc; HttpURLConnection httpc; this.hasStarted = true; this.startTime = new Date(); try { uWebResource = new URL(this.url); this.protocol = uWebResource.getProtocol(); } catch (MalformedURLException e) { this.failureCause = "Illegal Url"; this.errorMessage.append(e); this.wasSuccessful = false; this.finishTime = new Date(); this.hasFinished = true; return; } //-- try try { urlc = uWebResource.openConnection(); } catch (IOException e) { this.failureCause = "Couldnt create a connection"; //-- //this.errorMessage.append(e.getMessage()); this.errorMessage.append(e); this.wasSuccessful = false; this.finishTime = new Date(); this.hasFinished = true; return; } //-- try try { urlc.connect(); } catch (UnknownHostException e) { this.failureCause = "The host '" + uWebResource.getHost() + "' could not be found"; this.errorMessage.append(e); this.wasSuccessful = false; this.finishTime = new Date(); this.hasFinished = true; return; } catch (IOException e) { this.failureCause = "Couldnt establish a connection to URL"; this.errorMessage.append(e); this.wasSuccessful = false; this.finishTime = new Date(); this.hasFinished = true; return; } //-- try /* cast the connection object so as to find out the precise cause of any download problems */ if (uWebResource.getProtocol().equalsIgnoreCase("http")) { httpc = (HttpURLConnection)urlc; try { this.httpResponseCode = httpc.getResponseCode(); this.httpResponseMessage = httpc.getResponseMessage(); } catch (IOException e) { } if (this.httpResponseCode == 404) { this.finishTime = new Date(); this.wasSuccessful = false; this.hasFinished = true; this.failureCause = "file not found on server"; this.errorMessage.append( "Message from host '" + uWebResource.getHost() + "'" + NEWLINE + this.httpResponseCode + " " + this.httpResponseMessage); return; } if ((this.httpResponseCode < 200) || (this.httpResponseCode > 299)) { this.finishTime = new Date(); this.wasSuccessful = false; this.hasFinished = true; this.failureCause = "server refused request"; this.errorMessage.append(NEWLINE + "Server refused request : " + NEWLINE + "Server response code : " + this.httpResponseCode + NEWLINE + "Server response message: " + this.httpResponseMessage + NEWLINE); return; } this.httpContentType = httpc.getContentType(); this.httpContentLength = httpc.getContentLength(); this.httpContentEncoding = httpc.getContentEncoding(); //-- the static method below may be useful to do further checks //-- on the content type of the resource. //URLConnection.guessContentTypeFromStream(inputstream); } //-- if is http File fSaveFile; String sFileName; String sFileNameSuffix; if (this.httpContentType.startsWith("text")) { sFileNameSuffix = ".txt"; } else if (this.httpContentType.startsWith("application")) { sFileNameSuffix = ".app"; } else { sFileNameSuffix = ""; } //-- get path is 1.4 //-- StringBuffer sbUrlPath = new StringBuffer(uWebResource.getPath()); StringBuffer sbUrlPath = new StringBuffer(uWebResource.getFile()); //-- //-- if the url has no file name, add something if (sbUrlPath.toString().trim().endsWith("/")) { if (this.httpContentType.trim().equalsIgnoreCase("text/html")) { sbUrlPath.append(this.DEFAULT_FILE_NAME + ".html"); } else if (this.httpContentType.startsWith("text")) { sbUrlPath.append(this.DEFAULT_FILE_NAME + ".txt"); } else { sbUrlPath.append(this.DEFAULT_FILE_NAME + sFileNameSuffix); } } //-- if if (sbUrlPath.toString().trim().equalsIgnoreCase("")) { sbUrlPath.append(this.DEFAULT_FILE_NAME + sFileNameSuffix); } //-- get rid of the url path to just leave the file name //-- Under MS jview getName returns the full path not just //-- the last component //-- sFileName = (new File(sbUrlPath.toString())).getName(); //-- //-- Replace some potentially bad characters as well sFileName = sbUrlPath.toString().substring( sbUrlPath.toString().lastIndexOf('/') + 1) .replace('\\', '-') .replace('%', '-') .replace('=', '-') .replace('?', '-') .replace('*', '-'); //-- if no filename is given, use the Url filename, otherwise //-- use the given name. StringBuffer sbFileName; if (this.fileName == "") { sbFileName = new StringBuffer(sFileName); } else { sbFileName = new StringBuffer(this.fileName); } //-- prefix the directory name if (this.directoryName != "") { sbFileName.insert(0, this.directoryName + File.separator); } String sOriginalFileName = new String(sbFileName); boolean bFileExists = (new File(sbFileName.toString())).exists(); if (bFileExists && !this.regetExisting) { this.failureCause = "File exists and reget flag is false"; this.errorMessage.append( "A file of the given name already exists and the "); this.errorMessage.append( "'regetExisting' flag of the object is set to false. "); this.errorMessage.append( "In other words the object is not supposed to download "); this.errorMessage.append( "files where the local file already exists. "); this.errorMessage.append( "You can use the WebDownload.regetExisting() method"); this.errorMessage.append( "to change this behavior. "); this.wasSuccessful = false; this.finishTime = new Date(); this.hasFinished = true; return; } //-- //-- get a file name which doesnt exist already by //-- adding a numeric suffix //-- if (!this.overwriteFiles) { int ii = 0; while ((new File(sbFileName.toString())).exists()) { ii++; sbFileName.setLength(0); sbFileName.append(sOriginalFileName); sbFileName.append("." + Integer.toString(ii)); } } //-- if not overwrite this.fileName = sbFileName.toString(); fSaveFile = new File(sbFileName.toString()); if (fSaveFile.isDirectory()) { this.wasSuccessful = false; this.failureCause = "Local file name is a directory"; this.errorMessage.append( "The local file name '" + this.fileName + "' is a directory"); this.finishTime = new Date(); return; } InputStream isResource; FileOutputStream fout; try { isResource = uWebResource.openConnection().getInputStream(); } catch (IOException e) { this.failureCause = "Couldnt open the inputstream"; this.errorMessage.append(e); this.wasSuccessful = false; this.finishTime = new Date(); this.hasFinished = true; return; } try { fout = new FileOutputStream(fSaveFile); } catch (FileNotFoundException e) { this.failureCause = "Local save path is invalid"; this.errorMessage.append(e.toString()); this.errorMessage.append( " The path given for saving the downloaded file "); this.errorMessage.append("locally ("); this.errorMessage.append(fSaveFile.toString()); this.errorMessage.append(") is not valid. "); this.errorMessage.append( "This may mean that one or more of the directories in the "); this.errorMessage.append( "path does not exist. or that the file name has illegal"); this.errorMessage.append( "characters in it."); this.wasSuccessful = false; this.finishTime = new Date(); this.hasFinished = true; return; } //-- try catch (IOException e) { this.wasSuccessful = false; this.finishTime = new Date(); this.hasFinished = true; return; } //-- try int iByte; int iCounter = 0; try { iByte = isResource.read(); while (iByte != -1) { while ((iByte != -1) && (iCounter < this.CHUNK_SIZE)) { fout.write(iByte); iCounter++; iByte = isResource.read(); } //-- while this.bytesRead += iCounter; iCounter = 0; if (this.showProgress) { System.out.print(this.PROGRESS_MARKER); } } //-- while } catch (IOException e) { this.failureCause = "problem reading from the stream"; this.errorMessage.append(e); this.wasSuccessful = false; this.finishTime = new Date(); this.hasFinished = true; return; } //-- try try { isResource.close(); fout.close(); } catch (IOException e) { this.failureCause = "problem closing the streams"; this.errorMessage.append(e); this.wasSuccessful = false; this.finishTime = new Date(); this.hasFinished = true; return; } //-- try this.finishTime = new Date(); this.wasSuccessful = true; this.hasFinished = true; } //-- method: download() //-------------------------------------------- /** an alias to comply with the task interface */ public String printReport() { return this.printStatistics(); } //-------------------------------------------- /** prints information such as the start time, the finish * time, the duration and the success or failure of the * download. */ public String printStatistics() { StringBuffer sbOutput = new StringBuffer(""); sbOutput.append(NEWLINE); sbOutput.append("Attempted to get resource >"); sbOutput.append(this.url); sbOutput.append(NEWLINE); sbOutput.append("And saving to file name >"); sbOutput.append(this.fileName); sbOutput.append(NEWLINE); sbOutput.append("in the directory >"); sbOutput.append(this.directoryName); sbOutput.append(NEWLINE); if (this.wasSuccessful) { sbOutput.append("Result of the download >"); sbOutput.append("SUCCESS"); sbOutput.append(NEWLINE); } else { sbOutput.append("Result of the download >"); sbOutput.append("FAILURE"); sbOutput.append(NEWLINE); } sbOutput.append("Download duration (s) >"); sbOutput.append((float)this.getDownloadDuration()/1000); sbOutput.append(NEWLINE); sbOutput.append("Total bytes read >"); sbOutput.append(this.bytesRead); sbOutput.append(NEWLINE); sbOutput.append("Average download speed (kbyte/s) >"); sbOutput.append(this.getAverageSpeed()); sbOutput.append(NEWLINE); if (!this.failureCause.equals("")) { sbOutput.append("Cause of download failure >"); sbOutput.append(this.failureCause); sbOutput.append(NEWLINE); } sbOutput.append("Protocol used >"); sbOutput.append(this.protocol); sbOutput.append(NEWLINE); sbOutput.append("Http Content Type >"); sbOutput.append(this.httpContentType); sbOutput.append(NEWLINE); sbOutput.append("Http Content Length >"); sbOutput.append(this.httpContentLength); sbOutput.append(NEWLINE); sbOutput.append("Http Content Encoding >"); sbOutput.append(this.httpContentEncoding); sbOutput.append(NEWLINE); sbOutput.append("Start Time >"); sbOutput.append(this.startTime); sbOutput.append(NEWLINE); sbOutput.append("Finish Time >"); sbOutput.append(this.finishTime); sbOutput.append(NEWLINE); if (!this.errorMessage.toString().equals("")) { sbOutput.append("Error Messages generated during the download >"); sbOutput.append(NEWLINE); sbOutput.append(this.errorMessage.toString()); sbOutput.append(NEWLINE); } sbOutput.append(""); sbOutput.append(""); sbOutput.append(""); return sbOutput.toString(); } //-------------------------------------------- public String getFileName() { return this.fileName; } //-------------------------------------------- /* return the number of milliseconds taken to download the file. */ public long getDownloadDuration() { if (this.finishTime.getTime() == 0) { return 0; } if (this.startTime.getTime() == 0) { return 0; } return this.finishTime.getTime() - this.startTime.getTime(); } //-------------------------------------------- /** print a message to reassure the user */ public String printStartMessage() { StringBuffer sbReturn = new StringBuffer(""); sbReturn.append("Attempting to get " + this.url); if (this.showProgress) { sbReturn.append(", each [" + this.PROGRESS_MARKER + "]"); sbReturn.append("=" + this.CHUNK_SIZE + " bytes"); } else { sbReturn.append(" (no progress marker)"); } sbReturn.append(NEWLINE); return sbReturn.toString(); } //-- method: printStartMessage //-------------------------------------------- /* how is this different from toString */ public String print() { return this.printStatistics(); } //-------------------------------------------- /** a concise summary of what happened */ public String toString() { StringBuffer sbOutput = new StringBuffer(""); sbOutput.append("resource download "); sbOutput.append("(url="); sbOutput.append(this.url); sbOutput.append(")"); sbOutput.append(" --> \""); if (!this.directoryName.equals("")) { sbOutput.append(this.directoryName); sbOutput.append(File.separator); } sbOutput.append(this.fileName); sbOutput.append("\""); if (this.wasSuccessful) { sbOutput.append(" -successful-"); } else { sbOutput.append(" -failed-"); } sbOutput.append(" took:"); sbOutput.append((float)this.getDownloadDuration()/1000); sbOutput.append("s"); sbOutput.append(", size:"); sbOutput.append(this.bytesRead); //sbOutput.append(", speed (kb/s):"); //sbOutput.append(this.getAverageSpeed()); if (!this.httpContentType.equals("")) { sbOutput.append(", content:"); sbOutput.append(this.httpContentType); } sbOutput.append(" -at- "); sbOutput.append(this.startTime); sbOutput.append(NEWLINE); sbOutput.append(""); return sbOutput.toString(); } //-- method: toString() //-------------------------------------------- /** to allow for testing and one-off downloads */ public static void main(String[] args) throws Exception { String sUsageMessage = "usage: java WebDownload url [dir]"; String sDirectory = new String(""); if (args.length == 0) { System.out.println(sUsageMessage); System.exit(-1); } if (args.length == 2) { sDirectory = args[1]; } StringBuffer sOutput = new StringBuffer(""); String sTest = new String(args[0]); WebDownload wdTest = new WebDownload(sTest); wdTest.showProgress(); if (!wdTest.setLocalDirectory(sDirectory)) { System.out.println( "The directory you specified was not found,"); System.out.println( "using the current directory instead."); } System.out.println(wdTest.printStartMessage()); wdTest.download(); System.out.println(wdTest.printStatistics()); System.out.println(wdTest.toString()); } //-- main() } //-- WebDownload class