package be.nikiroo.utils; import java.io.ByteArrayOutputStream; 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.ArrayList; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import be.nikiroo.utils.streams.MarkableFileInputStream; /** * This class offer some utilities based around Streams and Files. * * @author niki */ public class IOUtils { /** * Write the data to the given {@link File}. * * @param in * the data source * @param target * the target {@link File} * * @return the number of bytes written * * @throws IOException * in case of I/O error */ public static long write(InputStream in, File target) throws IOException { OutputStream out = new FileOutputStream(target); try { return write(in, out); } finally { out.close(); } } /** * Write the data to the given {@link OutputStream}. * * @param in * the data source * @param out * the target {@link OutputStream} * * @return the number of bytes written * * @throws IOException * in case of I/O error */ public static long write(InputStream in, OutputStream out) throws IOException { long written = 0; byte buffer[] = new byte[4096]; int len = in.read(buffer); while (len > -1) { out.write(buffer, 0, len); written += len; len = in.read(buffer); } return written; } /** * Recursively Add a {@link File} (which can thus be a directory, too) to a * {@link ZipOutputStream}. * * @param zip * the stream * @param base * the path to prepend to the ZIP info before the actual * {@link File} path * @param target * the source {@link File} (which can be a directory) * @param targetIsRoot * FALSE if we need to add a {@link ZipEntry} for base/target, * TRUE to add it at the root of the ZIP * * @throws IOException * in case of I/O error */ public static void zip(ZipOutputStream zip, String base, File target, boolean targetIsRoot) throws IOException { if (target.isDirectory()) { if (!targetIsRoot) { if (base == null || base.isEmpty()) { base = target.getName(); } else { base += "/" + target.getName(); } zip.putNextEntry(new ZipEntry(base + "/")); } File[] files = target.listFiles(); if (files != null) { for (File file : files) { zip(zip, base, file, false); } } } else { if (base == null || base.isEmpty()) { base = target.getName(); } else { base += "/" + target.getName(); } zip.putNextEntry(new ZipEntry(base)); FileInputStream in = new FileInputStream(target); try { IOUtils.write(in, zip); } finally { in.close(); } } } /** * Zip the given source into dest. * * @param src * the source {@link File} (which can be a directory) * @param dest * the destination .zip file * @param srcIsRoot * FALSE if we need to add a {@link ZipEntry} for src, TRUE to * add it at the root of the ZIP * * @throws IOException * in case of I/O error */ public static void zip(File src, File dest, boolean srcIsRoot) throws IOException { OutputStream out = new FileOutputStream(dest); try { ZipOutputStream zip = new ZipOutputStream(out); try { IOUtils.zip(zip, "", src, srcIsRoot); } finally { zip.close(); } } finally { out.close(); } } /** * Unzip the given ZIP file into the target directory. * * @param zipFile * the ZIP file * @param targetDirectory * the target directory * * @return the number of extracted files (not directories) * * @throws IOException * in case of I/O errors */ public static long unzip(File zipFile, File targetDirectory) throws IOException { long count = 0; if (targetDirectory.exists() && targetDirectory.isFile()) { throw new IOException("Cannot unzip " + zipFile + " into " + targetDirectory + ": it is not a directory"); } targetDirectory.mkdir(); if (!targetDirectory.exists()) { throw new IOException("Cannot create target directory " + targetDirectory); } FileInputStream in = new FileInputStream(zipFile); try { ZipInputStream zipStream = new ZipInputStream(in); try { for (ZipEntry entry = zipStream.getNextEntry(); entry != null; entry = zipStream .getNextEntry()) { File file = new File(targetDirectory, entry.getName()); if (entry.isDirectory()) { file.mkdirs(); } else { IOUtils.write(zipStream, file); count++; } } } finally { zipStream.close(); } } finally { in.close(); } return count; } /** * Write the {@link String} content to {@link File}. * * @param dir * the directory where to write the {@link File} * @param filename * the {@link File} name * @param content * the content * * @throws IOException * in case of I/O error */ public static void writeSmallFile(File dir, String filename, String content) throws IOException { if (!dir.exists()) { dir.mkdirs(); } writeSmallFile(new File(dir, filename), content); } /** * Write the {@link String} content to {@link File}. * * @param file * the {@link File} to write * @param content * the content * * @throws IOException * in case of I/O error */ public static void writeSmallFile(File file, String content) throws IOException { FileOutputStream out = new FileOutputStream(file); try { out.write(StringUtils.getBytes(content)); } finally { out.close(); } } /** * Read the whole {@link File} content into a {@link String}. * * @param file * the {@link File} * * @return the content * * @throws IOException * in case of I/O error */ public static String readSmallFile(File file) throws IOException { InputStream stream = new FileInputStream(file); try { return readSmallStream(stream); } finally { stream.close(); } } /** * Read the whole {@link InputStream} content into a {@link String}. * * @param stream * the {@link InputStream} * * @return the content * * @throws IOException * in case of I/O error */ public static String readSmallStream(InputStream stream) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { write(stream, out); return out.toString("UTF-8"); } finally { out.close(); } } /** * Recursively delete the given {@link File}, which may of course also be a * directory. *
* Will either silently continue or throw an exception in case of error,
* depending upon the parameters.
*
* @param target
* the target to delete
* @param exception
* TRUE to throw an {@link IOException} in case of error, FALSE
* to silently continue
*
* @return TRUE if all files were deleted, FALSE if an error occurred
*
* @throws IOException
* if an error occurred and the parameters allow an exception to
* be thrown
*/
public static boolean deltree(File target, boolean exception)
throws IOException {
List
* Will silently continue in case of error.
*
* @param target
* the target to delete
*
* @return TRUE if all files were deleted, FALSE if an error occurred
*/
public static boolean deltree(File target) {
return deltree(target, null).isEmpty();
}
/**
* Recursively delete the given {@link File}, which may of course also be a
* directory.
*
* Will collect all {@link File} that cannot be deleted in the given
* accumulator.
*
* @param target
* the target to delete
* @param errorAcc
* the accumulator to use for errors, or NULL to create a new one
*
* @return the errors accumulator
*/
public static List