Java tutorial
/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URL; import java.util.logging.Logger; /** * A collection of file utilities. * * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> * @author <a href="mailto:Scott.Stark@jboss.org">Scott Stark<a/> * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a> * @version $Revision: 1958 $ */ public final class Files { /** for byte-to-hex conversions */ private static final char[] hexDigits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /** The default size of the copy buffer. */ public static final int DEFAULT_BUFFER_SIZE = 8192; /** Delete a file, or a directory and all of its contents. * * @param dir The directory or file to delete. * @return True if all delete operations were successfull. */ public static boolean delete(final File dir) { boolean success = true; File files[] = dir.listFiles(); if (files != null) { for (int i = 0; i < files.length; i++) { File f = files[i]; if (f.isDirectory() == true) { // delete the directory and all of its contents. if (delete(f) == false) { success = false; System.out.println("Failed to delete dir: " + f.getAbsolutePath()); } } // delete each file in the directory else if (f.delete() == false) { success = false; System.out.println("Failed to delete file: " + f.getAbsolutePath()); } } } // finally delete the directory if (dir.delete() == false) { success = false; System.out.println("Failed to delete dir: " + dir.getAbsolutePath()); } return success; } /** * Delete a file or directory and all of its contents. * * @param dirname The name of the file or directory to delete. * @return True if all delete operations were successfull. */ public static boolean delete(final String dirname) { return delete(new File(dirname)); } /** * Delete a directory contaning the given file and all its contents. * * @param filename a file or directory in the containing directory to delete * @return true if all delete operations were successfull, false if any * delete failed. */ public static boolean deleteContaining(final String filename) { File file = new File(filename); File containingDir = file.getParentFile(); return delete(containingDir); } /** * Copy a file. * * @param source Source file to copy. * @param target Destination target file. * @param buff The copy buffer. * * @throws IOException Failed to copy file. */ public static void copy(final File source, final File target, final byte buff[]) throws IOException { BufferedInputStream in = new BufferedInputStream(new FileInputStream(source)); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(target)); int read; try { while ((read = in.read(buff)) != -1) { out.write(buff, 0, read); } } finally { Streams.flush(out); Streams.close(in); Streams.close(out); } } /** * Copy a file. * * @param source Source file to copy. * @param target Destination target file. * @param size The size of the copy buffer. * * @throws IOException Failed to copy file. */ public static void copy(final File source, final File target, final int size) throws IOException { copy(source, target, new byte[size]); } /** * Copy a file. * * @param source Source file to copy. * @param target Destination target file. * * @throws IOException Failed to copy file. */ public static void copy(final File source, final File target) throws IOException { copy(source, target, DEFAULT_BUFFER_SIZE); } /** * Copy a remote/local URL to a local file * * @param src the remote or local URL * @param dest the local file * @throws IOException upon error */ public static void copy(URL src, File dest) throws IOException { System.out.println("Copying " + src + " -> " + dest); // Validate that the dest parent directory structure exists File dir = dest.getParentFile(); if (!dir.exists()) { if (!dir.mkdirs()) { throw new IOException("mkdirs failed for: " + dir.getAbsolutePath()); } } // Remove any existing dest content if (dest.exists()) { if (!Files.delete(dest)) { throw new IOException("delete of previous content failed for: " + dest.getAbsolutePath()); } } // Treat local and remote URLs the same // prepare streams, do the copy and flush InputStream in = new BufferedInputStream(src.openStream()); OutputStream out = new BufferedOutputStream(new FileOutputStream(dest)); Streams.copy(in, out); out.flush(); out.close(); in.close(); } /** * Used to encode any string into a string that is safe to use as * a file name on most operating systems. * * Use decodeFileName() to get back the original string. * * Copied by Adrian's org.jboss.mq.pm.file.PersistenceManager * and adapted to use hex instead of decimal digits * * @param name the filename to encode * @return a filesystem-friendly filename */ public static String encodeFileName(String name) { return encodeFileName(name, '@'); } /** * Used to decode a file system friendly filename produced * by encodeFileName() method, above. * * Copied by Adrian's org.jboss.mq.pm.file.PersistenceManager * and adapted to use hex instead of decimal digits * * Note: * Decoding will not work if encoding produced * multi-byte encoded characters. If this is truly * needed we'll have to revise the encoding. * * @param name the filename to decode * @return the original name */ public static String decodeFileName(String name) { return decodeFileName(name, '@'); } /** * See encodeFileName(String) above. * * @param name the filename to encode * @param escape the escape character to use * @return a filesystem-friendly filename */ public static String encodeFileName(String name, char escape) { StringBuffer rc = new StringBuffer(); for (int i = 0; i < name.length(); i++) { switch (name.charAt(i)) { // These are the safe characters... case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'h': case 'H': case 'i': case 'I': case 'j': case 'J': case 'k': case 'K': case 'l': case 'L': case 'm': case 'M': case 'n': case 'N': case 'o': case 'O': case 'p': case 'P': case 'q': case 'Q': case 'r': case 'R': case 's': case 'S': case 't': case 'T': case 'u': case 'U': case 'v': case 'V': case 'w': case 'W': case 'x': case 'X': case 'y': case 'Y': case 'z': case 'Z': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': case '-': case '_': case '.': rc.append(name.charAt(i)); break; // Any other character needs to be encoded. default: // We encode the characters as <esc>hh, // where <esc> is the passed escape character and // hh is the hex value of the UTF8 byte of the character. // You might get <esc>hh<esc>hh since UTF8 can produce multiple // bytes for a single character. try { byte data[] = ("" + name.charAt(i)).getBytes("UTF8"); for (int j = 0; j < data.length; j++) { rc.append(escape); rc.append(hexDigits[(data[j] >> 4) & 0xF]); // high order digit rc.append(hexDigits[(data[j]) & 0xF]); // low order digit } } catch (UnsupportedEncodingException wonthappen) { // nada } } } return rc.toString(); } /** * See decodeFileName(String) above. * * @param name the filename to decode * @param escape the escape character to use * @return the original name */ public static String decodeFileName(String name, char escape) { if (name == null) { return null; } StringBuffer sbuf = new StringBuffer(name.length()); for (int i = 0; i < name.length(); i++) { char c = name.charAt(i); if (c == escape) { char h1 = name.charAt(++i); char h2 = name.charAt(++i); // convert hex digits to integers int d1 = (h1 >= 'a') ? (10 + h1 - 'a') : ((h1 >= 'A') ? (10 + h1 - 'A') : (h1 - '0')); int d2 = (h2 >= 'a') ? (10 + h2 - 'a') : ((h2 >= 'A') ? (10 + h2 - 'A') : (h2 - '0')); // handling only the <esc>hh case here, as we don't know // if <esc>hh<esc>hh belong to the same character // (and we are lazy to change the encoding) - REVISIT byte[] bytes = new byte[] { (byte) (d1 * 16 + d2) }; try { String s = new String(bytes, "UTF8"); sbuf.append(s); } catch (UnsupportedEncodingException wonthappen) { // nada } } else { sbuf.append(c); } } return sbuf.toString(); } } final class Streams { private static final Logger log = Logger.getLogger("Streams.class"); ///////////////////////////////////////////////////////////////////////// // Closing // ///////////////////////////////////////////////////////////////////////// /** * Attempt to close an <tt>InputStream</tt>. * * @param stream <tt>InputStream</tt> to attempt to close. * @return <tt>True</tt> if stream was closed (or stream was null), * or <tt>false</tt> if an exception was thrown. */ public static boolean close(final InputStream stream) { // do not attempt to close null stream, but return sucess if (stream == null) { return true; } boolean success = true; try { stream.close(); } catch (IOException e) { success = false; } return success; } /** * Attempt to close an <tt>OutputStream</tt>. * * @param stream <tt>OutputStream</tt> to attempt to close. * @return <tt>True</tt> if stream was closed (or stream was null), * or <tt>false</tt> if an exception was thrown. */ public static boolean close(final OutputStream stream) { // do not attempt to close null stream, but return sucess if (stream == null) { return true; } boolean success = true; try { stream.close(); } catch (IOException e) { success = false; } return success; } /** * Attempt to close an <tt>InputStream</tt> or <tt>OutputStream</tt>. * * @param stream Stream to attempt to close. * @return <tt>True</tt> if stream was closed (or stream was null), * or <tt>false</tt> if an exception was thrown. * * @throws IllegalArgumentException Stream is not an <tt>InputStream</tt> * or <tt>OuputStream</tt>. */ public static boolean close(final Object stream) { boolean success = false; if (stream instanceof InputStream) { success = close((InputStream) stream); } else if (stream instanceof OutputStream) { success = close((OutputStream) stream); } else { throw new IllegalArgumentException("stream is not an InputStream or OutputStream"); } return success; } /** * Attempt to close an array of <tt>InputStream</tt>s. * * @param streams Array of <tt>InputStream</tt>s to attempt to close. * @return <tt>True</tt> if all streams were closed, or <tt>false</tt> * if an exception was thrown. */ public static boolean close(final InputStream[] streams) { boolean success = true; for (int i = 0; i < streams.length; i++) { boolean rv = close(streams[i]); if (!rv) success = false; } return success; } /** * Attempt to close an array of <tt>OutputStream</tt>s. * * @param streams Array of <tt>OutputStream</tt>s to attempt to close. * @return <tt>True</tt> if all streams were closed, or <tt>false</tt> * if an exception was thrown. */ public static boolean close(final OutputStream[] streams) { boolean success = true; for (int i = 0; i < streams.length; i++) { boolean rv = close(streams[i]); if (!rv) success = false; } return success; } /** * Attempt to close an array of <tt>InputStream</tt>a and/or * <tt>OutputStream</tt>s. * * @param streams Array of streams to attempt to close. * @return <tt>True</tt> if all streams were closed, or <tt>false</tt> * if an exception was thrown. * * @throws IllegalArgumentException Stream is not an <tt>InputStream</tt> * or <tt>OuputStream</tt>. Closing * stops at the last valid stream * object in this case. */ public static boolean close(final Object[] streams) { boolean success = true; for (int i = 0; i < streams.length; i++) { boolean rv = close(streams[i]); if (!rv) success = false; } return success; } /** * Attempt to flush and close an <tt>OutputStream</tt>. * * @param stream <tt>OutputStream</tt> to attempt to flush and close. * @return <tt>True</tt> if stream was flushed and closed, or * <tt>false</tt> if an exception was thrown. */ public static boolean fclose(final OutputStream stream) { return flush(stream) && close(stream); } /** * Attempt to flush and close an array of <tt>OutputStream</tt>s. * * @param streams <tt>OutputStream</tt>s to attempt to flush and close. * @return <tt>True</tt> if all streams were flushed and closed, * or <tt>false</tt> if an exception was thrown. */ public static boolean fclose(final OutputStream[] streams) { boolean success = true; for (int i = 0; i < streams.length; i++) { boolean rv = fclose(streams[i]); if (!rv) success = false; } return success; } ///////////////////////////////////////////////////////////////////////// // Flushing // ///////////////////////////////////////////////////////////////////////// /** * Attempt to flush an <tt>OutputStream</tt>. * * @param stream <tt>OutputStream</tt> to attempt to flush. * @return <tt>True</tt> if stream was flushed (or stream was null), * or <tt>false</tt> if an exception was thrown. */ public static boolean flush(final OutputStream stream) { // do not attempt to close null stream, but return sucess if (stream == null) { return true; } boolean success = true; try { stream.flush(); } catch (IOException e) { success = false; } return success; } /** * Attempt to flush an array of <tt>OutputStream</tt>s. * * @param streams <tt>OutputStream</tt>s to attempt to flush. * @return <tt>True</tt> if all streams were flushed, or <tt>false</tt> * if an exception was thrown. */ public static boolean flush(final OutputStream[] streams) { boolean success = true; for (int i = 0; i < streams.length; i++) { boolean rv = flush(streams[i]); if (!rv) success = false; } return success; } ///////////////////////////////////////////////////////////////////////// // Misc // ///////////////////////////////////////////////////////////////////////// /** The default buffer size that will be used for buffered operations. */ public static final int DEFAULT_BUFFER_SIZE = 2048; /** * Copy all of the bytes from the input stream to the output stream. * * @param input Stream to read bytes from. * @param output Stream to write bytes to. * @param buffer The buffer to use while copying. * @return The total number of bytes copied. * * @throws IOException Failed to copy bytes. */ public static long copy(final InputStream input, final OutputStream output, final byte buffer[]) throws IOException { long total = 0; int read; System.out.println("copying " + input + " to " + output + " with buffer size: " + buffer.length); while ((read = input.read(buffer)) != -1) { output.write(buffer, 0, read); total += read; System.out.println("bytes read: " + read + "; total bytes read: " + total); } return total; } /** * Copy all of the bytes from the input stream to the output stream. * * @param input Stream to read bytes from. * @param output Stream to write bytes to. * @param size The size of the buffer to use while copying. * @return The total number of bytes copied. * * @throws IOException Failed to copy bytes. */ public static long copy(final InputStream input, final OutputStream output, final int size) throws IOException { return copy(input, output, new byte[size]); } /** * Copy all of the bytes from the input stream to the output stream. * * @param input Stream to read bytes from. * @param output Stream to write bytes to. * @return The total number of bytes copied. * * @throws IOException Failed to copy bytes. */ public static long copy(final InputStream input, final OutputStream output) throws IOException { return copy(input, output, DEFAULT_BUFFER_SIZE); } /** * Copy all of the bytes from the input stream to the output stream * wrapping streams in buffers as needed. * * @param input Stream to read bytes from. * @param output Stream to write bytes to. * @return The total number of bytes copied. * * @throws IOException Failed to copy bytes. */ public static long copyb(InputStream input, OutputStream output) throws IOException { if (!(input instanceof BufferedInputStream)) { input = new BufferedInputStream(input); } if (!(output instanceof BufferedOutputStream)) { output = new BufferedOutputStream(output); } long bytes = copy(input, output, DEFAULT_BUFFER_SIZE); output.flush(); return bytes; } /** * Copy a limited number of bytes from the input stream to the * output stream. * * @param input Stream to read bytes from. * @param output Stream to write bytes to. * @param buffer The buffer to use while copying. * @param length The maximum number of bytes to copy. * @return The total number of bytes copied. * * @throws IOException Failed to copy bytes. */ public static long copySome(final InputStream input, final OutputStream output, final byte buffer[], final long length) throws IOException { long total = 0; int read; int readLength; // setup the initial readLength, if length is less than the buffer // size, then we only want to read that much readLength = Math.min((int) length, buffer.length); System.out.println("initial read length: " + readLength); while (readLength != 0 && (read = input.read(buffer, 0, readLength)) != -1) { System.out.println("read bytes: " + read); output.write(buffer, 0, read); total += read; System.out.println("total bytes read: " + total); // update the readLength readLength = Math.min((int) (length - total), buffer.length); System.out.println("next read length: " + readLength); } return total; } /** * Copy a limited number of bytes from the input stream to the * output stream. * * @param input Stream to read bytes from. * @param output Stream to write bytes to. * @param size The size of the buffer to use while copying. * @param length The maximum number of bytes to copy. * @return The total number of bytes copied. * * @throws IOException Failed to copy bytes. */ public static long copySome(final InputStream input, final OutputStream output, final int size, final long length) throws IOException { return copySome(input, output, new byte[size], length); } /** * Copy a limited number of bytes from the input stream to the * output stream. * * @param input Stream to read bytes from. * @param output Stream to write bytes to. * @param length The maximum number of bytes to copy. * @return The total number of bytes copied. * * @throws IOException Failed to copy bytes. */ public static long copySome(final InputStream input, final OutputStream output, final long length) throws IOException { return copySome(input, output, DEFAULT_BUFFER_SIZE, length); } }