001/* 002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 * Bogdan Stefanescu <bs@nuxeo.com> 019 * Estelle Giuly <egiuly@nuxeo.com> 020 */ 021package org.nuxeo.common.utils; 022 023import java.io.BufferedOutputStream; 024import java.io.Closeable; 025import java.io.File; 026import java.io.FileInputStream; 027import java.io.FileOutputStream; 028import java.io.IOException; 029import java.io.InputStream; 030import java.io.OutputStream; 031import java.io.UnsupportedEncodingException; 032import java.net.MalformedURLException; 033import java.net.URISyntaxException; 034import java.net.URL; 035import java.net.URLDecoder; 036import java.util.ArrayList; 037import java.util.Collection; 038import java.util.List; 039import java.util.zip.ZipInputStream; 040 041import org.apache.commons.io.Charsets; 042import org.apache.commons.io.IOUtils; 043import org.apache.commons.logging.Log; 044import org.apache.commons.logging.LogFactory; 045 046public final class FileUtils { 047 048 private static final Log log = LogFactory.getLog(FileUtils.class); 049 050 // This is an utility class 051 private FileUtils() { 052 } 053 054 /** 055 * @deprecated since 9.1 seems unused 056 */ 057 @Deprecated 058 public static void safeClose(Closeable stream) { 059 try { 060 stream.close(); 061 } catch (IOException e) { 062 // do nothing 063 } 064 } 065 066 /** 067 * @deprecated since 9.1 use {@link IOUtils#copy(InputStream, OutputStream)} instead. 068 */ 069 @Deprecated 070 public static void copy(InputStream in, OutputStream out) throws IOException { 071 IOUtils.copy(in, out); 072 } 073 074 /** 075 * @deprecated since 9.1 use {@link IOUtils#toByteArray(URL)} instead. 076 */ 077 @Deprecated 078 public static byte[] readBytes(URL url) throws IOException { 079 return IOUtils.toByteArray(url); 080 } 081 082 /** 083 * @deprecated since 9.1 use {@link IOUtils#toByteArray(InputStream)} instead. 084 */ 085 @Deprecated 086 public static byte[] readBytes(InputStream in) throws IOException { 087 return IOUtils.toByteArray(in); 088 } 089 090 /** 091 * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#readLines(File)} instead. 092 */ 093 @Deprecated 094 public static String readFile(File file) throws IOException { 095 return org.apache.commons.io.FileUtils.readFileToString(file, Charsets.UTF_8); 096 } 097 098 /** 099 * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#readLines(File)} instead. 100 */ 101 @Deprecated 102 public static List<String> readLines(File file) throws IOException { 103 return org.apache.commons.io.FileUtils.readLines(file); 104 } 105 106 /** 107 * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeLines(File, Collection)} instead. 108 */ 109 @Deprecated 110 public static void writeLines(File file, List<String> lines) throws IOException { 111 org.apache.commons.io.FileUtils.writeLines(file, lines); 112 } 113 114 /** 115 * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#readFileToByteArray(File)} instead. 116 */ 117 @Deprecated 118 public static byte[] readBytes(File file) throws IOException { 119 return org.apache.commons.io.FileUtils.readFileToByteArray(file); 120 } 121 122 /** 123 * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeByteArrayToFile(File, byte[])} instead. 124 */ 125 @Deprecated 126 public static void writeFile(File file, byte[] buf) throws IOException { 127 org.apache.commons.io.FileUtils.writeByteArrayToFile(file, buf); 128 } 129 130 /** 131 * @since 5.5 132 * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeByteArrayToFile(File, byte[], boolean)} 133 * instead. 134 */ 135 @Deprecated 136 public static void writeFile(File file, byte[] buf, boolean append) throws IOException { 137 org.apache.commons.io.FileUtils.writeByteArrayToFile(file, buf, append); 138 } 139 140 /** 141 * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeStringToFile(File, String)} instead. 142 */ 143 @Deprecated 144 public static void writeFile(File file, String buf) throws IOException { 145 org.apache.commons.io.FileUtils.writeStringToFile(file, buf); 146 } 147 148 /** 149 * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeStringToFile(File, String, boolean)} 150 * instead. 151 * @since 5.5 152 */ 153 @Deprecated 154 public static void writeFile(File file, String buf, boolean append) throws IOException { 155 org.apache.commons.io.FileUtils.writeStringToFile(file, buf, append); 156 } 157 158 /** 159 * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#copyURLToFile(URL, File)} instead. 160 */ 161 @Deprecated 162 public static void download(URL url, File file) throws IOException { 163 org.apache.commons.io.FileUtils.copyURLToFile(url, file); 164 } 165 166 /** 167 * @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#copyInputStreamToFile(InputStream, File)} 168 * instead. <strong>Note:</strong> that proposed method close the stream, could lead to issues with some 169 * input stream (like {@link ZipInputStream} which uses a global cursor on zip file stream when 170 * iterating on files in it). 171 */ 172 @Deprecated 173 public static void copyToFile(InputStream in, File file) throws IOException { 174 org.apache.commons.io.FileUtils.copyInputStreamToFile(in, file); 175 } 176 177 /** 178 * @deprecated since 9.1 Use {@link IOUtils#copy(InputStream, OutputStream)} instead. See 179 * {@link #append(InputStream, File, boolean)} for more information. 180 */ 181 @Deprecated 182 public static void append(File src, File dst) throws IOException { 183 append(src, dst, false); 184 } 185 186 /** 187 * @deprecated since 9.1 Use {@link IOUtils#copy(InputStream, OutputStream)} instead. See 188 * {@link #append(InputStream, File, boolean)} for more information. 189 */ 190 @Deprecated 191 public static void append(File src, File dst, boolean appendNewLine) throws IOException { 192 try (InputStream in = new FileInputStream(src)) { 193 append(in, dst, appendNewLine); 194 } 195 } 196 197 /** 198 * @deprecated since 9.1 Use {@link IOUtils#copy(InputStream, OutputStream)} instead. See 199 * {@link #append(InputStream, File, boolean)} for more information. 200 */ 201 @Deprecated 202 public static void append(InputStream in, File file) throws IOException { 203 append(in, file, false); 204 } 205 206 /** 207 * @deprecated since 9.1 Use {@link IOUtils#copy(InputStream, OutputStream)} instead. 208 */ 209 @Deprecated 210 public static void append(InputStream in, File file, boolean appendNewLine) throws IOException { 211 try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file, true))) { 212 if (appendNewLine) { 213 out.write(System.getProperty("line.separator").getBytes()); 214 } 215 IOUtils.copy(in, out); 216 } 217 } 218 219 /** 220 * Copies source to destination. If source and destination are the same, does nothing. Both single files and 221 * directories are handled. 222 * 223 * @param src the source file or directory 224 * @param dst the destination file or directory 225 * @throws IOException 226 */ 227 public static void copy(File src, File dst) throws IOException { 228 if (src.equals(dst)) { 229 return; 230 } 231 if (src.isFile()) { 232 copyFile(src, dst); 233 } else { 234 copyTree(src, dst); 235 } 236 } 237 238 public static void copy(File[] src, File dst) throws IOException { 239 for (File file : src) { 240 copy(file, dst); 241 } 242 } 243 244 public static void copyFile(File src, File dst) throws IOException { 245 if (dst.isDirectory()) { 246 dst = new File(dst, src.getName()); 247 } 248 org.apache.commons.io.FileUtils.copyFile(src, dst, false); 249 } 250 251 /** 252 * Copies recursively source to destination. 253 * <p> 254 * The source file is assumed to be a directory. 255 * 256 * @param src the source directory 257 * @param dst the destination directory 258 * @throws IOException 259 */ 260 public static void copyTree(File src, File dst) throws IOException { 261 if (src.isFile()) { 262 copyFile(src, dst); 263 } else if (src.isDirectory()) { 264 if (dst.exists()) { 265 dst = new File(dst, src.getName()); 266 dst.mkdir(); 267 } else { // allows renaming dest dir 268 dst.mkdirs(); 269 } 270 File[] files = src.listFiles(); 271 for (File file : files) { 272 copyTree(file, dst); 273 } 274 } 275 } 276 277 public static void copyTree(File src, File dst, PathFilter filter) throws IOException { 278 copyTree(src, dst, new Path("/"), filter); 279 } 280 281 public static void copyTree(File src, File dst, Path prefix, PathFilter filter) throws IOException { 282 if (!prefix.isAbsolute()) { 283 prefix = prefix.makeAbsolute(); 284 } 285 int rootIndex = src.getPath().length() + 1; 286 for (File file : src.listFiles()) { 287 copyTree(rootIndex, file, new File(dst, file.getName()), prefix, filter); 288 } 289 } 290 291 protected static void copyTree(int rootIndex, File src, File dst, Path prefix, PathFilter filter) 292 throws IOException { 293 if (src.isFile()) { 294 String relPath = src.getPath().substring(rootIndex); 295 if (!filter.accept(new Path(relPath))) { 296 return; 297 } 298 if (!prefix.isRoot()) { // remove prefix from path 299 String path = dst.getPath(); 300 String pff = prefix.toString(); 301 int prefixIndex = path.lastIndexOf(pff); 302 if (prefixIndex > 0) { 303 path = path.substring(0, prefixIndex) + path.substring(prefixIndex + pff.length()); 304 dst = new File(path.toString()); 305 } 306 } 307 dst.getParentFile().mkdirs(); 308 copyFile(src, dst); 309 } else if (src.isDirectory()) { 310 File[] files = src.listFiles(); 311 for (File file : files) { 312 copyTree(rootIndex, file, new File(dst, file.getName()), prefix, filter); 313 } 314 } 315 } 316 317 /** 318 * Decodes an URL path so that is can be processed as a filename later. 319 * 320 * @param url the Url to be processed. 321 * @return the decoded path. 322 */ 323 public static String getFilePathFromUrl(URL url) { 324 String path = ""; 325 if (url.getProtocol().equals("file")) { 326 try { 327 path = URLDecoder.decode(url.getPath(), "UTF-8"); 328 } catch (UnsupportedEncodingException e) { 329 log.error(e); 330 } 331 } 332 return path; 333 } 334 335 public static File getFileFromURL(URL url) { 336 File file; 337 String filename = getFilePathFromUrl(url); 338 if (filename.equals("")) { 339 file = null; 340 } else { 341 file = new File(filename); 342 } 343 return file; 344 } 345 346 public static String getParentPath(String path) { 347 int p = path.lastIndexOf(File.separator); 348 if (p == -1) { 349 return null; 350 } 351 return path.substring(0, p); 352 } 353 354 public static String getFileName(String path) { 355 int p = path.lastIndexOf(File.separator); 356 if (p == -1) { 357 return path; 358 } 359 return path.substring(p + 1); 360 } 361 362 public static String getFileExtension(String path) { 363 int p = path.lastIndexOf('.'); 364 if (p == -1) { 365 return null; 366 } 367 return path.substring(p + 1); 368 } 369 370 public static String getFileNameNoExt(String path) { 371 String name = getFileName(path); 372 int p = name.lastIndexOf('.'); 373 if (p == -1) { 374 return name; 375 } 376 return name.substring(0, p); 377 } 378 379 /** 380 * Retrieves the total path of a resource from the Thread Context. 381 * 382 * @param resource the resource name to be retrieved. 383 * @return the decoded path. 384 */ 385 public static String getResourcePathFromContext(String resource) { 386 URL url = Thread.currentThread().getContextClassLoader().getResource(resource); 387 return getFilePathFromUrl(url); 388 } 389 390 public static File getResourceFileFromContext(String resource) { 391 File file; 392 String filename = getResourcePathFromContext(resource); 393 if (filename.equals("")) { 394 file = null; 395 } else { 396 file = new File(filename); 397 } 398 return file; 399 } 400 401 public static File[] findFiles(File root, String pattern, boolean recurse) { 402 List<File> result = new ArrayList<>(); 403 if (pattern == null) { 404 if (recurse) { 405 collectFiles(root, result); 406 } else { 407 return root.listFiles(); 408 } 409 } else { 410 FileNamePattern pat = new FileNamePattern(pattern); 411 if (recurse) { 412 collectFiles(root, pat, result); 413 } else { 414 File[] files = root.listFiles(); 415 for (File file : files) { 416 if (pat.match(file.getName())) { 417 result.add(file); 418 } 419 } 420 } 421 } 422 return result.toArray(new File[result.size()]); 423 } 424 425 public static void collectFiles(File root, FileNamePattern pattern, List<File> result) { 426 File[] files = root.listFiles(); 427 for (File file : files) { 428 if (pattern.match(file.getName())) { 429 result.add(file); 430 if (file.isDirectory()) { 431 collectFiles(file, pattern, result); 432 } 433 } 434 } 435 } 436 437 public static void collectFiles(File root, List<File> result) { 438 File[] files = root.listFiles(); 439 for (File file : files) { 440 result.add(file); 441 if (file.isDirectory()) { 442 collectFiles(file, result); 443 } 444 } 445 } 446 447 /** 448 * @deprecated since 9.1 Use {@link IOUtils#closeQuietly(InputStream)} instead or {@link AutoCloseable} feature. 449 */ 450 @Deprecated 451 public static void close(InputStream in) { 452 if (in != null) { 453 try { 454 in.close(); 455 } catch (IOException e) { 456 log.error(e); 457 } 458 } 459 } 460 461 /** 462 * @deprecated since 9.1 Use {@link IOUtils#closeQuietly(InputStream)} instead or {@link AutoCloseable} feature. 463 */ 464 @Deprecated 465 public static void close(OutputStream out) { 466 if (out != null) { 467 try { 468 out.close(); 469 } catch (IOException e) { 470 log.error(e); 471 } 472 } 473 } 474 475 /** 476 * Create a file handler (this doesn't create a real file) given a file URI. This method can be used to create files 477 * from invalid URL strings (e.g. containing spaces ..) 478 * 479 * @return a file object 480 */ 481 public static File urlToFile(String url) throws MalformedURLException { 482 return urlToFile(new URL(url)); 483 } 484 485 public static File urlToFile(URL url) { 486 try { 487 return new File(url.toURI()); 488 } catch (URISyntaxException e) { 489 return new File(url.getPath()); 490 } 491 } 492 493 /** 494 * @deprecated since 9.1 Use {@link IOUtils#readLines(InputStream)} instead. 495 */ 496 @Deprecated 497 public static List<String> readLines(InputStream in) throws IOException { 498 return IOUtils.readLines(in); 499 } 500 501 /** 502 * Compares two files content as String even if their EOL are different 503 * 504 * @param expected a file content with Windows or Unix like EOL 505 * @param source another file content with Windows or Unix like EOL 506 * @return the result of equals after replacing their EOL 507 */ 508 public static boolean areFilesContentEquals(String expected, String source) { 509 if (expected == source) { 510 return true; 511 } 512 513 if (expected == null || source == null) { 514 return false; 515 } 516 517 if (expected.length() != source.length()) { 518 // Prevent from comparing files with Windows EOL 519 return expected.replace("\r\n", "\n").equals(source.replace("\r\n", "\n")); 520 } else { 521 return expected.equals(source); 522 } 523 } 524 525 /** 526 * Returns a safe filename, replacing unsafe characters (: \ / * ..) with "_". For instance, it turns 527 * "tmp/../2349:876398/foo.png" into "tmp___2349_876398_foo.png" 528 * 529 * @param filename the filename 530 * @return the safe filename with underscores instead of unsafe characters 531 * @since 9.1 532 */ 533 public static String getSafeFilename(String filename) { 534 return filename.replaceAll("(\\\\)|(\\/)|(\\:)|(\\*)|(\\.\\.)", "_"); 535 } 536 537}