Java tutorial
/** * Copyright (c) 2003 - 2007 OpenSubsystems s.r.o. Slovak Republic. All rights reserved. * * Project: OpenSubsystems * * $Id: FileUtils.java,v 1.12 2007/02/01 07:18:32 bastafidli Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 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.util.Arrays; import java.util.Date; import java.util.List; import java.util.Properties; import java.util.logging.Logger; /** * Collection of methods to make work with files easier. * * @version $Id: FileUtils.java,v 1.12 2007/02/01 07:18:32 bastafidli Exp $ * @author Miro Halas * @code.reviewer Miro Halas * @code.reviewed 1.7 2006/05/21 03:45:37 bastafidli */ public class FileUtils { // Configuration settings /////////////////////////////////////////////////// /** * Default 10 digit file storage distribution array. This means that if I * want to name file as 10 digit number e.g. number 123 as 0000000123 or * number 123456789 as 01234567890. Then the path constructed from number * 1234567890 using distribution 2/2/2/4 would be 12/34/56/0123456789 */ public static final int[] DEFAULT_STRORAGE_TREE_DISTRIBUTION = { 2, 2, 2, 4 }; /** * How big buffer to use to process files. */ public static final int BUFFER_SIZE = 65536; // Cached values //////////////////////////////////////////////////////////// /** * Temporary directory to use. It is guarantee that it ends with \ (or /) */ protected static String s_strTempDirectory; // Constructors ///////////////////////////////////////////////////////////// /** * Move file to a new location. If the destination is on different volume, * this file will be copied and then original file will be deleted. * If the destination already exists, this method renames it with different * name and leaves it in that directory and moves the new file along side * the renamed one. * * @param flCurrent - file to move * @param flDestination - destination file * @throws IOException - error message * @throws OSSException - error message */ public static void moveFile(File flCurrent, File flDestination) throws IOException { // Make sure that the source exist, it might be already moved from // a directory and we just don't know about it if (flCurrent.exists()) { // Next check if the destination file exists if (flDestination.exists()) { // If the destination exists, that means something went wrong // Rename the destination file under temporaty name and try to // move the new file instead of it renameToTemporaryName(flDestination, "old"); } // Make sure the directory exists and if not create it File flFolder; flFolder = flDestination.getParentFile(); if ((flFolder != null) && (!flFolder.exists())) { if (!flFolder.mkdirs()) { // Do not throw the exception if the directory already exists // because it was created meanwhile for example by a different // thread if (!flFolder.exists()) { throw new IOException("Cannot create directory " + flFolder); } } } // Now everything should exist so try to rename the file first // After testing, this renames files even between volumes C to H // so we don't have to do anything else on Windows but we still // have to handle erro on Unix if (!flCurrent.renameTo(flDestination)) { // Try to copy and delete since the rename doesn't work on Solaris // between file systems copyFile(flCurrent, flDestination); // Now delete the file if (!flCurrent.delete()) { // Delete the destination file first since we haven't really moved // the file flDestination.delete(); throw new IOException("Cannot delete already copied file " + flCurrent); } } } } /** * Copy the current file to the destination file. * * @param flCurrent - source file * @param flDestination - destination file * @throws IOException - error message * @throws OSSException - error message */ public static void copyFile(File flCurrent, File flDestination) throws IOException { // Make sure the directory exists and if not create it File flFolder; flFolder = flDestination.getParentFile(); if ((flFolder != null) && (!flFolder.exists())) { if (!flFolder.mkdirs()) { // Do not throw the exception if the directory already exists // because it was created meanwhile for example by a different // thread if (!flFolder.exists()) { throw new IOException("Cannot create directory " + flFolder); } } } // FileChannel srcChannel = null; // FileChannel dstChannel = null; FileInputStream finInput = null; //MHALAS: This code is not working reliably on Solaris 8 with 1.4.1_01 // Getting exceptions from native code /* // Create channel on the source srcChannel = new FileInputStream(flCurrent).getChannel(); // Create channel on the destination dstChannel = new FileOutputStream(flDestination).getChannel(); // Copy file contents from source to destination dstChannel.transferFrom(srcChannel, 0, srcChannel.size()); Don't forget to close the channels if you enable this code again */ try { finInput = new FileInputStream(flCurrent); } catch (IOException ioExec) { if (finInput != null) { try { finInput.close(); } catch (Throwable thr) { } } throw ioExec; } FileUtils.copyStreamToFile(finInput, flDestination); } /** * Rename the file to temporaty name with given prefix * * @param flFileToRename - file to rename * @param strPrefix - prefix to use * @throws IOException - error message */ public static void renameToTemporaryName(File flFileToRename, String strPrefix) throws IOException { assert strPrefix != null : "Prefix cannot be null."; String strParent; StringBuffer sbBuffer = new StringBuffer(); File flTemp; int iIndex = 0; strParent = flFileToRename.getParent(); // Generate new name for the file in a deterministic way do { iIndex++; sbBuffer.delete(0, sbBuffer.length()); if (strParent != null) { sbBuffer.append(strParent); sbBuffer.append(File.separatorChar); } sbBuffer.append(strPrefix); sbBuffer.append("_"); sbBuffer.append(iIndex); sbBuffer.append("_"); sbBuffer.append(flFileToRename.getName()); flTemp = new File(sbBuffer.toString()); } while (flTemp.exists()); // Now we should have unique name if (!flFileToRename.renameTo(flTemp)) { throw new IOException( "Cannot rename " + flFileToRename.getAbsolutePath() + " to " + flTemp.getAbsolutePath()); } } /** * Delete all files and directories in directory but do not delete the * directory itself. * * @param strDir - string that specifies directory to delete * @return boolean - sucess flag */ public static boolean deleteDirectoryContent(String strDir) { return ((strDir != null) && (strDir.length() > 0)) ? deleteDirectoryContent(new File(strDir)) : false; } /** * Delete all files and directories in directory but do not delete the * directory itself. * * @param fDir - directory to delete * @return boolean - sucess flag */ public static boolean deleteDirectoryContent(File fDir) { boolean bRetval = false; if (fDir != null && fDir.isDirectory()) { File[] files = fDir.listFiles(); if (files != null) { bRetval = true; boolean dirDeleted; for (int index = 0; index < files.length; index++) { if (files[index].isDirectory()) { // TODO: Performance: Implement this as a queue where you add to // the end and take from the beginning, it will be more efficient // than the recursion dirDeleted = deleteDirectoryContent(files[index]); if (dirDeleted) { bRetval = bRetval && files[index].delete(); } else { bRetval = false; } } else { bRetval = bRetval && files[index].delete(); } } } } return bRetval; } /** * Deletes all files and subdirectories under the specified directory including * the specified directory * * @param strDir - string that specifies directory to be deleted * @return boolean - true if directory was successfully deleted */ public static boolean deleteDir(String strDir) { return ((strDir != null) && (strDir.length() > 0)) ? deleteDir(new File(strDir)) : false; } /** * Deletes all files and subdirectories under the specified directory including * the specified directory * * @param fDir - directory to be deleted * @return boolean - true if directory was successfully deleted */ public static boolean deleteDir(File fDir) { boolean bRetval = false; if (fDir != null && fDir.exists()) { bRetval = deleteDirectoryContent(fDir); if (bRetval) { bRetval = bRetval && fDir.delete(); } } return bRetval; } /** * Compare binary files. Both files must be files (not directories) and exist. * * @param first - first file * @param second - second file * @return boolean - true if files are binery equal * @throws IOException - error in function */ public boolean isFileBinaryEqual(File first, File second) throws IOException { // TODO: Test: Missing test boolean retval = false; if ((first.exists()) && (second.exists()) && (first.isFile()) && (second.isFile())) { if (first.getCanonicalPath().equals(second.getCanonicalPath())) { retval = true; } else { FileInputStream firstInput = null; FileInputStream secondInput = null; BufferedInputStream bufFirstInput = null; BufferedInputStream bufSecondInput = null; try { firstInput = new FileInputStream(first); secondInput = new FileInputStream(second); bufFirstInput = new BufferedInputStream(firstInput, BUFFER_SIZE); bufSecondInput = new BufferedInputStream(secondInput, BUFFER_SIZE); int firstByte; int secondByte; while (true) { firstByte = bufFirstInput.read(); secondByte = bufSecondInput.read(); if (firstByte != secondByte) { break; } if ((firstByte < 0) && (secondByte < 0)) { retval = true; break; } } } finally { try { if (bufFirstInput != null) { bufFirstInput.close(); } } finally { if (bufSecondInput != null) { bufSecondInput.close(); } } } } } return retval; } /** * Get path which represents temporary directory. It is guarantee that it * ends with \ (or /). * * @return String */ public static String getTemporaryDirectory() { return s_strTempDirectory; } /** * Copy any input stream to output file. Once the data will be copied * the stream will be closed. * * @param input - InputStream to copy from * @param output - File to copy to * @throws IOException - error in function * @throws OSSMultiException - double error in function */ public static void copyStreamToFile(InputStream input, File output) throws IOException { FileOutputStream foutOutput = null; // open input file as stream safe - it can throw some IOException try { foutOutput = new FileOutputStream(output); } catch (IOException ioExec) { if (foutOutput != null) { try { foutOutput.close(); } catch (IOException ioExec2) { } } throw ioExec; } // all streams including os are closed in copyStreamToStream function // in any case copyStreamToStream(input, foutOutput); } /** * Copy any input stream to output stream. Once the data will be copied * both streams will be closed. * * @param input - InputStream to copy from * @param output - OutputStream to copy to * @throws IOException - io error in function * @throws OSSMultiException - double error in function */ public static void copyStreamToStream(InputStream input, OutputStream output) throws IOException { InputStream is = null; OutputStream os = null; int ch; try { if (input instanceof BufferedInputStream) { is = input; } else { is = new BufferedInputStream(input); } if (output instanceof BufferedOutputStream) { os = output; } else { os = new BufferedOutputStream(output); } while ((ch = is.read()) != -1) { os.write(ch); } os.flush(); } finally { IOException exec1 = null; IOException exec2 = null; try { // because this close can throw exception we do next close in // finally statement if (os != null) { try { os.close(); } catch (IOException exec) { exec1 = exec; } } } finally { if (is != null) { try { is.close(); } catch (IOException exec) { exec2 = exec; } } } if ((exec1 != null) && (exec2 != null)) { throw exec1; } else if (exec1 != null) { throw exec1; } else if (exec2 != null) { throw exec2; } } } }