/* * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Nuxeo - initial API and implementation * Bogdan Stefanescu <bs@nuxeo.com> * Estelle Giuly <egiuly@nuxeo.com> */ package org.nuxeo.common.utils; import java.io.BufferedOutputStream; import java.io.Closeable; 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.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.zip.ZipInputStream; import org.apache.commons.io.Charsets; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public final class FileUtils { private static final Log log = LogFactory.getLog(FileUtils.class); // This is an utility class private FileUtils() { } /** * @deprecated since 9.1 seems unused */ @Deprecated public static void safeClose(Closeable stream) { try { stream.close(); } catch (IOException e) { // do nothing } } /** * @deprecated since 9.1 use {@link IOUtils#copy(InputStream, OutputStream)} instead. */ @Deprecated public static void copy(InputStream in, OutputStream out) throws IOException { IOUtils.copy(in, out); } /** * @deprecated since 9.1 use {@link IOUtils#toByteArray(URL)} instead. */ @Deprecated public static byte[] readBytes(URL url) throws IOException { return IOUtils.toByteArray(url); } /** * @deprecated since 9.1 use {@link IOUtils#toByteArray(InputStream)} instead. */ @Deprecated public static byte[] readBytes(InputStream in) throws IOException { return IOUtils.toByteArray(in); } /** * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#readLines(File)} instead. */ @Deprecated public static String readFile(File file) throws IOException { return org.apache.commons.io.FileUtils.readFileToString(file, Charsets.UTF_8); } /** * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#readLines(File)} instead. */ @Deprecated public static List<String> readLines(File file) throws IOException { return org.apache.commons.io.FileUtils.readLines(file); } /** * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeLines(File, Collection)} instead. */ @Deprecated public static void writeLines(File file, List<String> lines) throws IOException { org.apache.commons.io.FileUtils.writeLines(file, lines); } /** * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#readFileToByteArray(File)} instead. */ @Deprecated public static byte[] readBytes(File file) throws IOException { return org.apache.commons.io.FileUtils.readFileToByteArray(file); } /** * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeByteArrayToFile(File, byte[])} instead. */ @Deprecated public static void writeFile(File file, byte[] buf) throws IOException { org.apache.commons.io.FileUtils.writeByteArrayToFile(file, buf); } /** * @since 5.5 * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeByteArrayToFile(File, byte[], boolean)} * instead. */ @Deprecated public static void writeFile(File file, byte[] buf, boolean append) throws IOException { org.apache.commons.io.FileUtils.writeByteArrayToFile(file, buf, append); } /** * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeStringToFile(File, String)} instead. */ @Deprecated public static void writeFile(File file, String buf) throws IOException { org.apache.commons.io.FileUtils.writeStringToFile(file, buf); } /** * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeStringToFile(File, String, boolean)} * instead. * @since 5.5 */ @Deprecated public static void writeFile(File file, String buf, boolean append) throws IOException { org.apache.commons.io.FileUtils.writeStringToFile(file, buf, append); } /** * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#copyURLToFile(URL, File)} instead. */ @Deprecated public static void download(URL url, File file) throws IOException { org.apache.commons.io.FileUtils.copyURLToFile(url, file); } /** * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#copyInputStreamToFile(InputStream, File)} * instead. <strong>Note:</strong> that proposed method close the stream, could lead to issues with some * input stream (like {@link ZipInputStream} which uses a global cursor on zip file stream when * iterating on files in it). */ @Deprecated public static void copyToFile(InputStream in, File file) throws IOException { org.apache.commons.io.FileUtils.copyInputStreamToFile(in, file); } /** * @deprecated since 9.1 Use {@link IOUtils#copy(InputStream, OutputStream)} instead. See * {@link #append(InputStream, File, boolean)} for more information. */ @Deprecated public static void append(File src, File dst) throws IOException { append(src, dst, false); } /** * @deprecated since 9.1 Use {@link IOUtils#copy(InputStream, OutputStream)} instead. See * {@link #append(InputStream, File, boolean)} for more information. */ @Deprecated public static void append(File src, File dst, boolean appendNewLine) throws IOException { try (InputStream in = new FileInputStream(src)) { append(in, dst, appendNewLine); } } /** * @deprecated since 9.1 Use {@link IOUtils#copy(InputStream, OutputStream)} instead. See * {@link #append(InputStream, File, boolean)} for more information. */ @Deprecated public static void append(InputStream in, File file) throws IOException { append(in, file, false); } /** * @deprecated since 9.1 Use {@link IOUtils#copy(InputStream, OutputStream)} instead. */ @Deprecated public static void append(InputStream in, File file, boolean appendNewLine) throws IOException { try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file, true))) { if (appendNewLine) { out.write(System.getProperty("line.separator").getBytes()); } IOUtils.copy(in, out); } } /** * Copies source to destination. If source and destination are the same, does nothing. Both single files and * directories are handled. * * @param src the source file or directory * @param dst the destination file or directory * @throws IOException */ public static void copy(File src, File dst) throws IOException { if (src.equals(dst)) { return; } if (src.isFile()) { copyFile(src, dst); } else { copyTree(src, dst); } } public static void copy(File[] src, File dst) throws IOException { for (File file : src) { copy(file, dst); } } public static void copyFile(File src, File dst) throws IOException { if (dst.isDirectory()) { dst = new File(dst, src.getName()); } org.apache.commons.io.FileUtils.copyFile(src, dst, false); } /** * Copies recursively source to destination. * <p> * The source file is assumed to be a directory. * * @param src the source directory * @param dst the destination directory * @throws IOException */ public static void copyTree(File src, File dst) throws IOException { if (src.isFile()) { copyFile(src, dst); } else if (src.isDirectory()) { if (dst.exists()) { dst = new File(dst, src.getName()); dst.mkdir(); } else { // allows renaming dest dir dst.mkdirs(); } File[] files = src.listFiles(); for (File file : files) { copyTree(file, dst); } } } public static void copyTree(File src, File dst, PathFilter filter) throws IOException { copyTree(src, dst, new Path("/"), filter); } public static void copyTree(File src, File dst, Path prefix, PathFilter filter) throws IOException { if (!prefix.isAbsolute()) { prefix = prefix.makeAbsolute(); } int rootIndex = src.getPath().length() + 1; for (File file : src.listFiles()) { copyTree(rootIndex, file, new File(dst, file.getName()), prefix, filter); } } protected static void copyTree(int rootIndex, File src, File dst, Path prefix, PathFilter filter) throws IOException { if (src.isFile()) { String relPath = src.getPath().substring(rootIndex); if (!filter.accept(new Path(relPath))) { return; } if (!prefix.isRoot()) { // remove prefix from path String path = dst.getPath(); String pff = prefix.toString(); int prefixIndex = path.lastIndexOf(pff); if (prefixIndex > 0) { path = path.substring(0, prefixIndex) + path.substring(prefixIndex + pff.length()); dst = new File(path.toString()); } } dst.getParentFile().mkdirs(); copyFile(src, dst); } else if (src.isDirectory()) { File[] files = src.listFiles(); for (File file : files) { copyTree(rootIndex, file, new File(dst, file.getName()), prefix, filter); } } } /** * Decodes an URL path so that is can be processed as a filename later. * * @param url the Url to be processed. * @return the decoded path. */ public static String getFilePathFromUrl(URL url) { String path = ""; if (url.getProtocol().equals("file")) { try { path = URLDecoder.decode(url.getPath(), "UTF-8"); } catch (UnsupportedEncodingException e) { log.error(e); } } return path; } public static File getFileFromURL(URL url) { File file; String filename = getFilePathFromUrl(url); if (filename.equals("")) { file = null; } else { file = new File(filename); } return file; } public static String getParentPath(String path) { int p = path.lastIndexOf(File.separator); if (p == -1) { return null; } return path.substring(0, p); } public static String getFileName(String path) { int p = path.lastIndexOf(File.separator); if (p == -1) { return path; } return path.substring(p + 1); } public static String getFileExtension(String path) { int p = path.lastIndexOf('.'); if (p == -1) { return null; } return path.substring(p + 1); } public static String getFileNameNoExt(String path) { String name = getFileName(path); int p = name.lastIndexOf('.'); if (p == -1) { return name; } return name.substring(0, p); } /** * Retrieves the total path of a resource from the Thread Context. * * @param resource the resource name to be retrieved. * @return the decoded path. */ public static String getResourcePathFromContext(String resource) { URL url = Thread.currentThread().getContextClassLoader().getResource(resource); return getFilePathFromUrl(url); } public static File getResourceFileFromContext(String resource) { File file; String filename = getResourcePathFromContext(resource); if (filename.equals("")) { file = null; } else { file = new File(filename); } return file; } public static File[] findFiles(File root, String pattern, boolean recurse) { List<File> result = new ArrayList<>(); if (pattern == null) { if (recurse) { collectFiles(root, result); } else { return root.listFiles(); } } else { FileNamePattern pat = new FileNamePattern(pattern); if (recurse) { collectFiles(root, pat, result); } else { File[] files = root.listFiles(); for (File file : files) { if (pat.match(file.getName())) { result.add(file); } } } } return result.toArray(new File[result.size()]); } public static void collectFiles(File root, FileNamePattern pattern, List<File> result) { File[] files = root.listFiles(); for (File file : files) { if (pattern.match(file.getName())) { result.add(file); if (file.isDirectory()) { collectFiles(file, pattern, result); } } } } public static void collectFiles(File root, List<File> result) { File[] files = root.listFiles(); for (File file : files) { result.add(file); if (file.isDirectory()) { collectFiles(file, result); } } } /** * @deprecated since 9.1 Use {@link IOUtils#closeQuietly(InputStream)} instead or {@link AutoCloseable} feature. */ @Deprecated public static void close(InputStream in) { if (in != null) { try { in.close(); } catch (IOException e) { log.error(e); } } } /** * @deprecated since 9.1 Use {@link IOUtils#closeQuietly(InputStream)} instead or {@link AutoCloseable} feature. */ @Deprecated public static void close(OutputStream out) { if (out != null) { try { out.close(); } catch (IOException e) { log.error(e); } } } /** * Create a file handler (this doesn't create a real file) given a file URI. This method can be used to create files * from invalid URL strings (e.g. containing spaces ..) * * @return a file object */ public static File urlToFile(String url) throws MalformedURLException { return urlToFile(new URL(url)); } public static File urlToFile(URL url) { try { return new File(url.toURI()); } catch (URISyntaxException e) { return new File(url.getPath()); } } /** * @deprecated since 9.1 Use {@link IOUtils#readLines(InputStream)} instead. */ @Deprecated public static List<String> readLines(InputStream in) throws IOException { return IOUtils.readLines(in); } /** * Compares two files content as String even if their EOL are different * * @param expected a file content with Windows or Unix like EOL * @param source another file content with Windows or Unix like EOL * @return the result of equals after replacing their EOL */ public static boolean areFilesContentEquals(String expected, String source) { if (expected == source) { return true; } if (expected == null || source == null) { return false; } if (expected.length() != source.length()) { // Prevent from comparing files with Windows EOL return expected.replace("\r\n", "\n").equals(source.replace("\r\n", "\n")); } else { return expected.equals(source); } } /** * Returns a safe filename, replacing unsafe characters (: \ / * ..) with "_". For instance, it turns * "tmp/../2349:876398/foo.png" into "tmp___2349_876398_foo.png" * * @param filename the filename * @return the safe filename with underscores instead of unsafe characters * @since 9.1 */ public static String getSafeFilename(String filename) { return filename.replaceAll("(\\\\)|(\\/)|(\\:)|(\\*)|(\\.\\.)", "_"); } }