All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.velexio.jlegos.util.FileUtils Maven / Gradle / Ivy

Go to download

Java Legos - A collection of common application functionality for Java projects

There is a newer version: 0.17.6
Show newest version
package com.velexio.jlegos.util;

import com.velexio.jlegos.exceptions.ChecksumGenerationException;
import com.velexio.jlegos.exceptions.EnsureDirectoryException;

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * Contains some helpful utility methods for handling file operations.
 */
public class FileUtils {
    private static final int MAX_COPY_BUFFER = 8388608;
    private static final int MIN_COPY_BUFFER = 4;
    private static final int MAX_ZIP_BUFFER = 4194304;
    private static final int MIN_ZIP_BUFFER = 1024;

    private static int copyBufferSize = 4096;
    private static int zipBufferSize = 16384;

    private static byte[] copyBuffer = new byte[copyBufferSize];
    private static byte[] zipBuffer = new byte[zipBufferSize];

    /**
     * 

* Will change the copy operations (includes move) buffer size to a custom value. *

* * @param newBufferSize Tasks int value to indicate the size of the buffer (in bytes) to use for further operations. Default 4k, min 512 (bytes). */ public static void changeCopyBufferSize(int newBufferSize) { if (newBufferSize < MAX_COPY_BUFFER) { copyBufferSize = Math.max(newBufferSize, MIN_COPY_BUFFER); } else { copyBufferSize = MAX_COPY_BUFFER; } copyBuffer = new byte[copyBufferSize]; } /** * Will change the buffer size used during zip* operations. Default is 16k (16384) * @param newSize The new size to use */ public static void setZipBufferSize(int newSize) { if (newSize < MAX_ZIP_BUFFER) { zipBufferSize = Math.max(newSize, MIN_ZIP_BUFFER); } else { zipBufferSize = MAX_ZIP_BUFFER; } zipBuffer = new byte[zipBufferSize]; } /** * Will retrieve the current value of the copyBufferSize value begin used for copy/move operations * @return int value that indicates the size of the buffer (in bytes) */ public static int getCopyBufferSize() { return copyBufferSize; } public static boolean exists(String filePath) { File file = new File(filePath); return file.exists(); } /** * Determines if the file at path is a directory * * @param dirPath The path to the directory to check * @return boolean */ public static boolean isDir(String dirPath) { File file = new File(dirPath); return file.isDirectory(); } /** * Determines if the file at path is a file (not a directory) * * @param filePath The path to the file to check * @return boolean */ public static boolean isFile(String filePath) { File file = new File(filePath); return file.isFile(); } /** * Determines if the file at path is a hidden file * * @param filePath The path to the file to check * @return boolean */ public static boolean isHidden(String filePath) { File file = new File(filePath); return file.isHidden(); } /** * Returns boolean based on if file is writable by process * * @param filePath String path to the file * @return boolean */ public static boolean isWriteable(String filePath) { File file = new File(filePath); return isWriteable(file); } /** * Returns boolean based on if file is writable by process * * @param file File object to check * @return boolean */ public static boolean isWriteable(File file) { return file.canWrite(); } /** * Returns boolean based on if file is readable by process * * @param filePath String path to the file * @return boolean */ public static boolean isReadable(String filePath) { File file = new File(filePath); return isReadable(file); } /** * Returns boolean base on if file is readable by process * * @param file File object to check * @return boolean */ public static boolean isReadable(File file) { return file.canRead(); } /** * Will copy file. * @param source A string representing the path of the file to be copied * @param dest A string representing the path of the destination file * @param options Optional varargs that can be one or more of the {@link com.velexio.jlegos.util.FileCopyOption} enum values. If no options are * specified, a default of FileCopyOption.COPY_ATTRIBUTES will be applied. * @throws IOException Occurs when I/O operation fails * @throws FileNotFoundException If the source file is not present or not a file object * @see com.velexio.jlegos.util.FileCopyOption */ public static void copyFile(String source, String dest, FileCopyOption ... options) throws IOException { if (!isFile(source)) { throw new FileNotFoundException("The file [ " + source + "] does not exist. Copy terminated"); } List copyOptions = new ArrayList<>(List.of(StandardCopyOption.COPY_ATTRIBUTES)); if (options.length > 0) { copyOptions = new ArrayList<>(); for (FileCopyOption option : options) { copyOptions.add(option.getNioEquiv()); } } CopyOption[] finalCopyOptions = new CopyOption[copyOptions.size()]; finalCopyOptions = copyOptions.toArray(finalCopyOptions); Path sourcePath = Paths.get(source); Path destPath = Paths.get(dest); Files.copy(sourcePath, destPath, finalCopyOptions); } /** *

* Simple wrapper around file rename, but convenience of not having to initialize File objects. *

* * @param currentPath String representing the path of the current file * @param newPath String to represent the new name of the file */ public static void rename(String currentPath, String newPath) { File source = new File(currentPath); File dest = new File(newPath); source.renameTo(dest); } /** * A nice utility if you need to make sure a directory exists and if not, have it created. * This method will overwrite if a regular file exists where directory is intended to go. * Use the "ensureDirectory" method if you want a safe method. * @param directoryPath The path where the directory should exist * @throws EnsureDirectoryException if the underlying regular file could not be removed. */ public static void ensureDirectoryForce(String directoryPath) throws EnsureDirectoryException { String blockingFileErrMessage = "A regular file exists where directory [" + directoryPath + "] would be placed and could not be overwritten"; try { FileUtils.ensureDirectory(directoryPath); } catch (EnsureDirectoryException ede) { File file = new File(directoryPath); if (!file.delete()) { throw new EnsureDirectoryException(blockingFileErrMessage); } else { try { FileUtils.ensureDirectory(directoryPath); } catch (EnsureDirectoryException nede) { throw new EnsureDirectoryException(blockingFileErrMessage); } } } } /** * A utility for ensuring that a directory exists, and if not, will attempt to create. * * @param directoryPath The path where the directory is to be created * @throws EnsureDirectoryException If there is a regular file present that matches the directory path */ public static void ensureDirectory(String directoryPath) throws EnsureDirectoryException { File dir = new File(directoryPath); if (dir.exists()) { if (!dir.isDirectory()) { throw new EnsureDirectoryException(dir); } } else { if (!dir.mkdirs()) { throw new EnsureDirectoryException("A regular file exists where directory [" + directoryPath + "] would be placed and could not be overwritten"); } } } /** * Quick utility that is both type-safe and easier to remember then using System.getProperty("java.io.tmpdir"). * * @return String that represents path to os specific temp directory */ public static String getTempDir() { return System.getProperty("java.io.tmpdir"); } /** * Simple touch command to either create an empty file if it does not exist, or to change the last modified date if it does exits. * Meant to work similar to the unix touch command. * * @param filePath The path to either where you want new empty file created or path to existing file to touch * @throws IOException On issue creating/modifying the file */ public static void touch(String filePath) throws IOException { File file = new File(filePath); if (!file.exists()) { if (!file.createNewFile()) { FileWriter fw = new FileWriter(file); fw.write(""); fw.close(); } } else { Date nowDate = new Date(); if (!file.setLastModified(nowDate.getTime())) { throw new IOException("Unable to 'touch' existing file [ " + file.getAbsolutePath() + "]. Check permissions and try again"); } } } /** *

* Simple delete for a single file. *

* * @param filePath The string that represents the path to the file * @throws IOException Thrown if there is an IO issue during delete */ public static void delete(String filePath) throws IOException { File file = new File(filePath); if (!file.delete()) { throw new IOException("Unable to delete file [ " + filePath + "]. Verify file exists and permissions set correctly"); } } /** * Simple utility to make easier for simple append to file operations. Less boilerplate than creating FileWriter, * remembering to close, etc. *

* Marked for removal in version 0.15.2 * * @param filePath String representation of the path to the file you want to append to * @param appendValue String value that you want appended to the file * @param asNewLine Set as true if the appended value should be set on a new line in the file. False will just append to the end of the file. * @throws IOException Will be raised if there is an issue writing to the file * @deprecated Please use write or writeLines methods */ @Deprecated(forRemoval = true) public static void append(String filePath, String appendValue, boolean asNewLine) throws IOException { File appendFile = new File(filePath); FileWriter fw = new FileWriter(appendFile, true); if (asNewLine && appendFile.length() > 0) { appendValue = System.lineSeparator() + appendValue; } fw.append(appendValue); fw.close(); } /** * Method that will write a list of strings to a file. To stay as flexible as possible, if you would line each line in the list to be * added to a new line, you will need to include the line separator. The line separator (although most common use case) is not added so * that this method can be used for use cases where each line is just appended to end of file. * * @param filePath A string representing the path to the file * @param lines A list of strings to be written to the file * @throws IOException */ public static void writeLines(String filePath, List lines) throws IOException { File appendFile = new File(filePath); FileWriter fw = new FileWriter(appendFile, true); for (String line : lines) { fw.append(line); } fw.close(); } /** * Will write a string value to a file. Content can be written with either append mode or not. If append is set to false, then writing begins * at first line of file. * * @param filePath A string representing the path to the file * @param content The content that is to be written * @param append boolean to indicate if content should be appended or not * @throws IOException */ public static void write(String filePath, String content, boolean append) throws IOException { File file = new File(filePath); FileWriter fw = new FileWriter(file, append); fw.write(content); fw.close(); } /** * Generates checksum of the file. Default algorithm is SHA-512. If you need a different one, use the overloaded method that allows you to * specify an algorithm * * @param filePath The path to the file that checksum will be performed on * @return String representing the checksum value * @throws ChecksumGenerationException if there is IO issues */ public static String checksum(String filePath) throws ChecksumGenerationException { return checksum(filePath, "SHA-512"); } /** * Generates a checksum of file represented by the passed path String * * @param filePath The string path representing the location of the file to checksum * @param digestAlgorithm The algorithm to use. Needs to be a valid algorithm that the standard java.security.MessageDigest class will accept * @return A String object representing the checksum value * @throws ChecksumGenerationException If there is either IO issues or passed an invalid MessageDigest algorithm */ public static String checksum(String filePath, String digestAlgorithm) throws ChecksumGenerationException { try { MessageDigest digest = MessageDigest.getInstance(digestAlgorithm); FileInputStream fis = new FileInputStream(filePath); byte[] byteArr = new byte[4096]; int byteCount; while ((byteCount = fis.read(byteArr)) > -1) { digest.update(byteArr, 0, byteCount); } fis.close(); byte[] csBytes = digest.digest(); return IntStream.range(0, csBytes.length) .mapToObj(i -> Integer.toString((csBytes[i] & 0xff) + 0x100, 16).substring(1)) .collect(Collectors.joining()); } catch (NoSuchAlgorithmException | IOException e) { throw new ChecksumGenerationException(); } } /** *

* Will create a directory at location specified by the parameter. If the parent directories do not exist, then they will be created. Similar to * linux command "mkdir -p dir path" *

* * @param dirPath The path where the directory will be created * @throws IOException If there is an IO error during operation */ public static void createDirectory(String dirPath) throws IOException { File dir = new File(dirPath); if (!dir.mkdirs()) { throw new IOException("Unable to create directory"); } } /** *

* Will recursively copy a directory to destination directory *

* * @param sourceDir Directory path to copy * @param targetDir Target parent directory will source directory will be written * @throws IOException */ public static void copyDirectory(String sourceDir, String targetDir, FileCopyOption... options) throws IOException { Path sourcePath = Paths.get(sourceDir); Path targetPath = Paths.get(targetDir); List copyOptions = new ArrayList<>(List.of(StandardCopyOption.COPY_ATTRIBUTES)); if (options.length > 0) { copyOptions = new ArrayList<>(); for (FileCopyOption option : options) { copyOptions.add(option.getNioEquiv()); } } final CopyOption[] finalCopyOptions = copyOptions.toArray(new CopyOption[0]); Files.walkFileTree(sourcePath, new SimpleFileVisitor() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { Files.createDirectories(targetPath.resolve(sourcePath.relativize(dir))); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.copy(file, targetPath.resolve(sourcePath.relativize(file)), finalCopyOptions); return FileVisitResult.CONTINUE; } }); } /** * Removes a directory in a recursive fashion. Meaning it will not only remove non-empty directories (unlike file.delete), but it will also * follow any sub-directories and remove those as well. * * @param directoryPath A string representing the path to the directory that is to be removed * @throws IOException If any of the files/sub-dirs cannot be deleted. */ public static void deleteDirectory(String directoryPath) throws IOException { File d = new File(directoryPath); List delinquentList = new ArrayList<>(); if (d.isDirectory()) { String[] fileList = d.list(); if (fileList != null) { for (String fp : fileList) { File f = new File(directoryPath + "/" + fp); if (f.isDirectory()) { FileUtils.deleteDirectory(f.getAbsolutePath()); } else { if (!f.delete()) { delinquentList.add(f.getAbsolutePath()); } } } } if (!d.delete()) { delinquentList.add(d.getAbsolutePath()); } } if (delinquentList.size() > 0) { throw new IOException(getDelinquentListErrorMessage(delinquentList)); } } /** * This simply empties the contents of the directory, but DOES NOT remove the top level directory represented by the passed in path. Note * it will remove all sub-directories * * @param directoryPath The path to the directory to empty * @throws IOException If there is an issue removing a file and/or subdirectory */ public static void emptyDirectory(String directoryPath) throws IOException { File d = new File(directoryPath); List delinquentList = new ArrayList<>(); if (d.isDirectory()) { String[] dirFileList = d.list(); if (dirFileList != null) { for (String fp : dirFileList) { File f = new File(fp); if (f.isDirectory()) { try { FileUtils.deleteDirectory(f.getAbsolutePath()); } catch (IOException ioe) { delinquentList.add(f.getAbsolutePath()); } } else { if (!f.delete()) { delinquentList.add(f.getAbsolutePath()); } } } } } if (delinquentList.size() > 0) { throw new IOException(getDelinquentListErrorMessage(delinquentList)); } } /** *

* Will get a count of all immediate regular files (non-dirs) that are directly in * the directory (This does not search subfolders) *

* * @param directoryPath * @return */ public static int getDirectoryFileCount(String directoryPath) { int count = 0; List fileList = getDirectoryFiles(directoryPath); for (File f : fileList) { if (f.isFile()) { count++; } } return count; } /** *

* Will get a count of immediate directories in specified folder path. Will NOT perform * the count recursively. *

* * @param directoryPath String representation of full path to the directory * @return int */ public static int getDirectoryFolderCount(String directoryPath) { int count = 0; List fileList = getDirectoryFiles(directoryPath); for (File f : fileList) { if (f.isDirectory()) { count++; } } return count; } /** * Gets the files of the specified directory (Including subdirectories). * * @param directoryPath The string representation of the path to the directory * @return A List of File objects representing the directories contents. Note, only first-level contents are retrieved */ public static List getDirectoryFiles(String directoryPath) { List fileList = new ArrayList<>(); File d = new File(directoryPath); if (d.isDirectory()) { fileList = Arrays.asList(d.listFiles()); } return fileList; } /** * Will get all the first level files from directory that match the specified pattern. The pattern is just a simple string matching pattern. * This method will not recognize regex, so just include a simple string that will match the file. *

* Example: * .txt will match all files that contain a .txt (file1.txt, file2.txt and file.txt.old * * @param directoryPath The path to the directory holding files * @param matchPattern A simple string match that will be matched against any portion of the file name * @return A List of File objects that represents all the files matching the pattern */ public static List getDirectoryFiles(String directoryPath, String matchPattern) { List fileList = new ArrayList<>(); File d = new File(directoryPath); if (d.isDirectory()) { File[] files = d.listFiles((dir, name) -> name.contains(matchPattern)); if (files != null) { fileList = Arrays.asList(files); } } return fileList; } /** * Will unzip a compressed file inside it's parent folder. * @param zipFilePath The path to the file that is to be unzipped * @throws IOException Thrown if there is an issue reading / writing during the operation */ public static void unzip(String zipFilePath) throws IOException { File zipfileParentDir = new File(zipFilePath).getParentFile(); ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath)); ZipEntry zipEntry = zis.getNextEntry(); while (zipEntry != null) { File entryFile = new File(zipfileParentDir, zipEntry.getName()); if (!entryFile.getCanonicalPath().startsWith(zipfileParentDir.getCanonicalPath() + FileUtils.getFileSeparator())) { throw new IOException("Zip entry [ " + zipEntry.getName() + "] is outside the target directory...unable to uncompress"); } if (zipEntry.getName().endsWith("/")) { FileUtils.ensureDirectoryForce(entryFile.getAbsolutePath()); } else { FileOutputStream fos = new FileOutputStream(entryFile); int len; while ((len = zis.read(zipBuffer)) > 0) { fos.write(zipBuffer, 0, len); } fos.close(); } zipEntry = zis.getNextEntry(); } zis.closeEntry(); zis.close(); } /** *

* Takes a file path and compresses the file with same file name, adding the .zip file extension. If the file has an existing extension, it is * replaced with the .zip. Meaning if file named file1.txt, then file1.zip will be compressed filename. If no extension, * the .zip is added i.e. file1 will just have ".zip" added file1.zip. *

*

* Note: An extension is considered to be any alphanumeric value that is 2-3 chars in length, proceeded by period "." character. *

* * @param filePath A string representation of the path to the file * @throws IOException If the file does not exist, cannot be read or the containing directory does not allow writes */ public static void zipFile(String filePath) throws IOException { File file = new File(filePath); if (!file.exists()) { throw new IOException("File [ " + filePath + "] does not exist"); } String parentDir = file.getAbsolutePath().substring(0, file.getAbsolutePath().lastIndexOf("/") + 1); String zipFilename = file.getName() + ".zip"; String matchPattern = "([\\S\\s]+)\\.([\\w]{2,3}$)"; if (file.getName().matches(matchPattern)) { zipFilename = file.getName().replaceAll(matchPattern, "$1.zip" ); } String fullZipPath = parentDir + zipFilename; FileInputStream fis = new FileInputStream(file); FileOutputStream fos = new FileOutputStream(fullZipPath); ZipOutputStream zos = new ZipOutputStream(fos); ZipEntry zipEntry = new ZipEntry(file.getName()); zos.putNextEntry(zipEntry); int len; while ((len = fis.read(zipBuffer)) > -1) { zos.write(zipBuffer, 0, len); } zos.close(); fis.close(); fos.close(); if (new File(fullZipPath).exists()) { file.delete(); } } /** * This method will create a multi-part file that contains multiple files. Source files do not need to be in same location. You need to specify * the name of the zip file as well as the destination directory. * @param zipFiles A List of String objects that represent the path to the files that are to be included * @param destDirectoryPath String that represents the full path to the directory where the zip file will be created * @param zipFilename The name for the multi-part zip file. i.e. allFiles.zip, my-project.zip * @throws IOException If the path that represents the destination directory either does not exist or is not a directory */ public static void zipFiles(List zipFiles, String destDirectoryPath, String zipFilename) throws IOException { File dir = new File(destDirectoryPath); if (!dir.isDirectory()) { throw new IOException("The path provided [ " + destDirectoryPath + "] either does not exist or not a directory."); } String multiZipFilename = destDirectoryPath + "/" + zipFilename; FileOutputStream fos = new FileOutputStream(multiZipFilename); ZipOutputStream zos = new ZipOutputStream(fos); for (String srcFilePath : zipFiles) { File file = new File(srcFilePath); FileInputStream fis = new FileInputStream(file); ZipEntry zipEntry = new ZipEntry(file.getName()); zos.putNextEntry(zipEntry); int len; while ((len = fis.read(zipBuffer)) > -1 ) { zos.write(zipBuffer, 0, len); } fis.close(); } zos.close(); fos.close(); } /** *

* Will compress an entire directory. The compressed file will be placed in the parent directory and will be named with the same name, with the * ".zip" extension. Original directory object will remain intact. *

* *

* Note: Any hidden files will not be included in the zip file *

* * @param directoryPath A string object representing the path to the directory to be compressed * @throws IOException Will be thrown if path does not represent a directory or any other IO error occurs */ public static void zipDirectory(String directoryPath) throws IOException { File dir = new File(directoryPath); String zipDirFilename = dir.getParentFile().getAbsolutePath() + "/" + dir.getName() + ".zip"; if (!dir.isDirectory()) { throw new IOException("The directory path provided [ " + directoryPath + "] either does not exist or not a directory object. Unable to proceed with compression routine."); } FileOutputStream fos = new FileOutputStream(zipDirFilename); ZipOutputStream zos = new ZipOutputStream(fos); zipDirFile(dir, dir.getName(), zos); zos.close(); fos.close(); } /* Recursive method for compressing all of directory contents */ private static void zipDirFile(File fileToZip, String fileName, ZipOutputStream zipOutStream) throws IOException { if (fileToZip.isHidden()) { return; } if (fileToZip.isDirectory()) { if (fileName.endsWith("/")) { zipOutStream.putNextEntry(new ZipEntry(fileName)); } else { zipOutStream.putNextEntry(new ZipEntry(fileName + "/")); } zipOutStream.closeEntry(); File[] childFiles = fileToZip.listFiles(); for (File file : childFiles) { zipDirFile(file, fileName + "/" + file.getName(), zipOutStream); } return; } FileInputStream fis = new FileInputStream(fileToZip); ZipEntry zipEntry = new ZipEntry(fileName); zipOutStream.putNextEntry(zipEntry); int length; while ((length = fis.read(zipBuffer)) > -1 ) { zipOutStream.write(zipBuffer, 0, length); } fis.close(); } /** *

* Will return the size of the file in gigabytes *

* @param filePath The string that represents the path to the file * @return Size of the file * @throws IOException If the file does not exist or is not a regular file */ public static double sizeGB(String filePath) throws IOException { File file = new File(filePath); if (!file.exists() || !file.isFile()) { throw new IOException("File [ " + filePath + "] is not a regular file."); } return (double) file.length() / (1024 * 1024 * 1024); } /** *

* Will return the size of the specified file in megabytes *

* * @param filePath The path of the file to get size * @return Size of the file in megabytes * @throws IOException If the file does not exist or is not a regular file */ public static double sizeMB(String filePath) throws IOException { File file = new File(filePath); if (!file.exists() || !file.isFile()) { throw new IOException("File [ " + filePath + "] is not a regular file."); } return (double) file.length() / (1024 * 1024); } /** *

* Will return the size of the specified file in kilobytes *

* * @param filePath The path of the file to get size * @return Size of the file in kilobytes * @throws IOException If the file does not exist or is not a regular file */ public static double sizeKB(String filePath) throws IOException { File file = new File(filePath); if (!file.exists() || !file.isFile()) { throw new IOException("File [ " + filePath + "] is not a regular file."); } return (double) file.length() / 1024; } /** *

* Will return the size of the specified file in bytes *

* * @param filePath The path of the file to get size * @return Size of the file in bytes * @throws IOException If the file does not exist or is not a regular file */ public static double sizeBytes(String filePath) throws IOException { File file = new File(filePath); if (!file.exists() || !file.isFile()) { throw new IOException("File [ " + filePath + "] is not a regular file."); } return (double) file.length(); } /** * Simple helper method to get os dependent file separator for those of us that cannot seem to remember the System.getProperty name. * @return The OS dependent file separator */ public static String getFileSeparator() { return System.getProperty("file.separator"); } /* A dry method to de-duplicate code used in emptyDirectory methods */ private static String getDelinquentListErrorMessage(List delinquentList) { String newLine = System.lineSeparator(); StringBuilder errMessage = new StringBuilder(); errMessage.append("Unable to remove the following files/subdirectories. Check permissions and try again:"); errMessage.append(newLine); for (String fileName : delinquentList) { errMessage.append(fileName); errMessage.append(newLine); } return errMessage.toString(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy