/* * [MiniFileTransfer.java] * * Summary: copy a file, typically from a resource in a jar to the hard disk. * * Copyright: (c) 1999-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 1999-09-05 implement download and copy. * use of readBytesBlocking to replace readFully and read. * 1.1 1999-10-05 implement upload with PUT, and a status check. * 1.2 1999-10-26 split into MiniFileTransfer, FileTransfer, * and MaxiFileTransfer * added support for ZipFile entry download. * 1.3 1999-10-28 add safety code when length specified * is -1 or 0, to copy as if the length were unknown. * 1.4 1999-10-29 ensure every file closed, including ZipFile * safety check for null parms. * 1.5 2001-01-23 use more elegant * reads in the while loops instead of breaks. * 1.6 2002-04-22 conforming package name. * 1.7 2003-09-15 add closeTarget parameter to many methods * rename download( InputStream, File ) to copy( InputStream , File ) * add copy( File, OutputStream ) * 1.8 2006-01-10 add Download class. * 1.9 2006-02-05 reformat with IntelliJ, add Javadoc * 2.0 2007-05-17 add pad and icon * 2.1 2007-08-04 convert to JDK 1.5, * add timeout support * add sleep to avoid hogging CPU on stall * add support for preferred MIME types on download. * 2.2 2007-08-24 use http Read methods. * 2.3 2007-09-26 add timeout. * 2.4 2008-07-05 add append method. * 2.5 2008-08-10 add setReadTimeout and setConnectTimeout methods. */ package com.mindprod.filetransfer; import com.mindprod.common18.Build; import com.mindprod.http.Read; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import static java.lang.System.*; /** * copy a file, typically from a resource in a jar to the hard disk. *

* To read or write from the client's local hard disk in an Applet, * you will need a signed Applet and security clearance. see Signed Applet in the Java glossary. To read a files from * the server, the file must be given public read access, usually the default. * * @author Roedy Green, Canadian Mind Products * @version 2.5 2008-08-10 add setReadTimeout and setConnectTimeout methods. * @since 1999-09-05 */ @SuppressWarnings( { "WeakerAccess" } ) public class MiniFileTransfer { /** * true if want debugging output */ static final boolean DEBUGGING = false; /** * default size of chunks to transfer at a time. */ private static final int DEFAULT_BUFFSIZE = 63 * 1024; static { System.setProperty( "java.net.preferIPv4Stack", "true" ); System.setProperty( "jsse.enableSNIExtension", "false" ); System.setProperty( "jdk.tls.ephemeralDHKeySize", "2048" ); } /** * size of chunks to transfer at a time. */ final int buffSize; /** * Allow 50 seconds to connect */ int connectTimeout = Build.CONNECT_TIMEOUT; /** * Allow 40 seconds for a read to go without progress */ int readTimeout = Build.READ_TIMEOUT; /** * constructor */ @SuppressWarnings( { "WeakerAccess" } ) public MiniFileTransfer() { this.buffSize = DEFAULT_BUFFSIZE; } /** * constructor * * @param buffSize how big the I/O chunks are to copy files. */ public MiniFileTransfer( int buffSize ) { if ( buffSize < 512 ) { buffSize = 512; } this.buffSize = buffSize; } /** * dummy main * * @param args not used source url, target file */ public static void main( String[] args ) { err.println( "MiniFileTransfer is not intended to be run from the command line. See Download." ); } /** * copy a file from a stream, typically a resource in the archive jar file to a local file on hard disk. * * @param source resource as stream e.g. this.class.getResourceAsStream("lyrics.ram"); Netscape interferes with * extensions *.exe, *.dll etc. So use *.ram for your resources. * @param target new file to be created on local hard disk. * * @return true if the copy was successful. */ @SuppressWarnings( { "BooleanMethodNameMustStartWithQuestion", "WeakerAccess" } ) public boolean copy( InputStream source, File target ) { if ( source == null ) { return false; } if ( target == null ) { return false; } final FileOutputStream os; try { // O P E N _ T A R G E T os = new FileOutputStream( target ); // C O P Y _ S O U R C E _ T O _ T A R G E T return copy( source, os, true ); // C L O S E // handled by copy. } catch ( IOException e ) { return false; } } // end download /** * Copy an InputStream to an OutputStream, until EOF. Use only when you don't know the length of the transfer ahead * of time. Otherwise use FileTransfer.copy. * * @param source InputStream, always left closed * @param target OutputStream * @param closeTarget true if you want target stream closed when done. false, leave the target open for more I/O. * * @return true if the copy was successful. */ @SuppressWarnings( { "NestedAssignment", "BooleanMethodNameMustStartWithQuestion" } ) public boolean copy( InputStream source, OutputStream target, boolean closeTarget ) { if ( source == null ) { return false; } if ( target == null ) { return false; } try { // R E A D / W R I T E by chunks int chunkSize = buffSize; // code will work even when chunkSize = 0 or chunks = 0; // Even for small files, we allocate a big buffer, since we // don't know the size ahead of time. byte[] ba = new byte[ chunkSize ]; // keep reading till hit eof int bytesRead; while ( ( bytesRead = Read.readBytesBlocking( source, ba, 0, chunkSize ) ) > 0 ) { target.write( ba, 0/* offset */, bytesRead/* len */ ); } // end while // C L O S E source.close(); if ( closeTarget ) { target.close(); } } catch ( IOException e ) { return false; } // all was ok return true; } // end copy /** * Copy a file from a resource in some a local jar file, not the archive, to a local file on hard disk. To deal with * remote jars in JDK 1.1 download the entire jar, then copy the various contents to their resting places, then * delete the jar. To deal with remote jars in JDK 1.2 use the new jar URL syntax, that lets you treat a member as * if it were an separate file, jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class * * @param sourceJar ZipFile e.g. new ZipFile("stuff.jar"), left open. * @param zipEntryString fully qualified name of ZipEntry e.g. "com/mindprod/mypack/Stuff.html". Note this is a * String, not a ZipEntry. * @param target new file to be created on local hard disk. * * @return true if the copy was successful. */ @SuppressWarnings( { "BooleanMethodNameMustStartWithQuestion" } ) public boolean copy( ZipFile sourceJar, String zipEntryString, File target ) { if ( sourceJar == null ) { return false; } if ( zipEntryString == null ) { return false; } if ( target == null ) { return false; } try { ZipEntry zipEntry = sourceJar.getEntry( zipEntryString ); if ( zipEntry == null ) { return false; } InputStream is = sourceJar.getInputStream( zipEntry ); return is != null && copy( is, target ); // C L O S E // download closes is and target // don't close entire zip with sourceJar.close(); } catch ( IOException e ) { return false; } } // end copy /** * override the default connect timeout of 50 seconds * * @param connectTimeout timeout to connect in ms. Note int not long. */ public void setConnectTimeout( int connectTimeout ) { this.connectTimeout = connectTimeout; } /** * override the default read timeout of 40 seconds * * @param readTimeout timeout to connect int ms. Note int not long. */ public void setReadTimeout( int readTimeout ) { this.readTimeout = readTimeout; } }