1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.utils;
14 import java.io.BufferedReader;
15 import java.io.Closeable;
17 import java.io.FileFilter;
18 import java.io.FileInputStream;
19 import java.io.FileNotFoundException;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.OutputStream;
25 import java.io.RandomAccessFile;
26 import java.io.UnsupportedEncodingException;
29 import java.nio.channels.FileChannel;
30 import java.nio.charset.Charset;
31 import java.nio.file.FileVisitResult;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.nio.file.SimpleFileVisitor;
35 import java.nio.file.StandardCopyOption;
36 import java.nio.file.attribute.BasicFileAttributes;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Deque;
40 import java.util.LinkedList;
41 import java.util.Random;
42 import java.util.zip.DataFormatException;
43 import java.util.zip.Deflater;
44 import java.util.zip.Inflater;
45 import java.util.zip.ZipEntry;
46 import java.util.zip.ZipException;
47 import java.util.zip.ZipInputStream;
48 import java.util.zip.ZipOutputStream;
50 import org.simantics.databoard.Bindings;
51 import org.simantics.databoard.adapter.AdaptException;
52 import org.simantics.databoard.adapter.Adapter;
53 import org.simantics.databoard.adapter.AdapterConstructionException;
54 import org.simantics.databoard.binding.Binding;
55 import org.simantics.databoard.type.Datatype;
56 import org.simantics.utils.bytes.LEInt;
57 import org.simantics.utils.strings.FileNameUtils;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
62 * Utilities for common file operations.
64 * see StreamUtil in databoard for Input/OutputStream reading/writing utils
65 * @see FileNameUtils for more utils
67 * @author Toni Kalajainen
68 * @author Tuukka Lehtonen
70 public class FileUtils {
72 private static final Logger LOGGER = LoggerFactory.getLogger(FileUtils.class);
75 * Create escaped filename
77 * @param name any string
78 * @return file compatible string
80 public static String escapeFileName(String name) {
82 return java.net.URLEncoder.encode(name, "UTF-8");
83 } catch (UnsupportedEncodingException e) {
85 throw new RuntimeException(e);
90 * Unescape filename into string
95 public static String unescapeFileName(String filename) {
97 return java.net.URLDecoder.decode(filename, "UTF-8");
98 } catch (UnsupportedEncodingException e) {
100 throw new RuntimeException(e);
104 public static File ensureParentDirectoryExists(String path) throws IOException {
105 return ensureParentDirectoryExists(new File(path));
109 * Ensures the parent directory pointed by the specified file path exists as a
112 * @param path the directory whose existence is to be ensured
113 * @return the requested directory
114 * @throws IOException if the specified directory cannot be created or
115 * already exists as a file
117 public static File ensureParentDirectoryExists(File path) throws IOException {
118 return ensureDirectoryExists(path.getParentFile());
122 public static File ensureDirectoryExists(String path) throws IOException {
123 return ensureDirectoryExists(new File(path));
127 * Ensures the directory pointed by the specified file path exists as a
130 * @param path the directory whose existence is to be ensured
131 * @return the requested directory
132 * @throws IOException if the specified directory cannot be created or
133 * already exists as a file
135 public static File ensureDirectoryExists(File path) throws IOException {
136 if (path.isDirectory())
137 // Already exists, everything OK.
141 // Path is not a directory but it exists, fail!
142 throw new IOException("file '" + path + "', already exists but it is not a directory");
146 // Path is not a directory but it exists, fail!
147 throw new IOException("could not create directory '" + path + "' for an unknown reason");
149 // Directory created OK.
154 * Copies the contents of a source file to the destination file.
156 * @param sourceFile the source file descriptor
157 * @param destFile the destination file descriptor
158 * @throws IOException when anything IO-related goes wrong during the copy
160 public static void copyFile(File sourceFile, File destFile) throws IOException {
161 if (!destFile.exists()) {
162 destFile.createNewFile();
165 FileInputStream fis = null;
166 FileOutputStream fos = null;
168 fis = new FileInputStream(sourceFile);
169 fos = new FileOutputStream(destFile);
170 FileChannel source = fis.getChannel();
171 FileChannel destination = fos.getChannel();
174 long size = source.size();
175 while (count < size) {
176 count += destination.transferFrom(source, count, size - count);
189 * Copy file or dir recursively.
193 * @throws IOException
195 public static void copy(File src, File dst) throws IOException
197 if (src.isDirectory()) {
198 if(!dst.exists()) dst.mkdir();
200 for (String file : src.list()) {
201 File srcFile = new File(src, file);
202 File dstFile = new File(dst, file);
203 copy(srcFile,dstFile);
211 // public static File createTempFileFromResource(URL sourceUrl) throws IOException {
212 // sourceUrl = FileLocator.resolve(sourceUrl);
215 // if (sourceUrl.getProtocol().equalsIgnoreCase("file"))
216 // sourceFile = new File(sourceUrl.getPath());
218 // sourceFile = new File(sourceUrl.toURI());
219 // } catch (URISyntaxException e) {
220 // throw new RuntimeException(e);
223 // File tempFile = File.createTempFile("tmp", ".tmp");
224 // FileUtils.copyFile(sourceFile, tempFile);
229 * Reads entire binary file
231 * @return contents of binary file
232 * @throws IOException on i/o problems
234 public static byte[] readFile(File file)
237 try (FileInputStream fis = new FileInputStream(file)) {
238 long size = file.length();
239 if (size>Integer.MAX_VALUE)
240 throw new IOException("File too big");
241 int len = (int) size;
242 byte data [] = new byte[len];
246 int read = fis.read(data, pos, len-pos);
254 * Creates and writes a binary file
257 * @throws IOException on i/o problems
259 public static void writeFile(File file, byte[] data)
262 // file.createNewFile();
263 // file.setWritable(true);
264 try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
265 raf.setLength(data.length);
273 * Fetch the entire contents of the input stream text file, and return it in
274 * a String. This style of implementation does not throw Exceptions to the
277 * @param stream the stream to completely read in
279 public static String getContents(InputStream stream) throws IOException {
280 return getContents(stream, Charset.defaultCharset());
284 * Fetch the entire contents of the input stream text file, and return it in
285 * a String. This style of implementation does not throw Exceptions to the
288 * @param stream the stream to completely read in
290 public static String getContents(InputStream stream, Charset charset, String lineSeparator) throws IOException {
291 StringBuilder contents = new StringBuilder();
292 BufferedReader input = null;
294 input = new BufferedReader(new InputStreamReader(stream, charset));
297 while ((line = input.readLine()) != null) {
298 contents.append(line);
299 contents.append(lineSeparator);
304 // flush and close both "input" and its underlying
308 } catch (IOException ex) {
309 ex.printStackTrace();
312 return contents.toString();
315 public static String getContents(File f) throws IOException {
316 return getContents(f, Charset.defaultCharset());
319 public static String getContents(File f, Charset charset) throws IOException {
320 return getContents(new FileInputStream(f), charset);
323 public static String getContents(InputStream stream, Charset charset) throws IOException {
324 return getContents(stream, charset, System.getProperty("line.separator"));
327 public static String getContents(InputStream stream, String lineSeparator) throws IOException {
328 return getContents(stream, Charset.defaultCharset(), lineSeparator);
332 * Fetch the entire contents of a text file, and return it in a String. This
333 * style of implementation does not throw Exceptions to the caller.
335 * @param sourcePath is a file which already exists and can be read.
337 public static String getContents(String sourcePath) throws IOException {
338 return getContents(sourcePath, Charset.defaultCharset(), System.getProperty("line.separator"));
341 public static String getContents(String sourcePath, String lineSeparator) throws IOException {
342 return getContents(sourcePath, Charset.defaultCharset(), lineSeparator);
345 public static String getContents(String sourcePath, Charset charset) throws IOException {
346 return getContents(sourcePath, charset, System.getProperty("line.separator"));
350 * Fetch the entire contents of a text file, and return it in a String. This
351 * style of implementation does not throw Exceptions to the caller.
353 * @param sourcePath is a file which already exists and can be read.
355 public static String getContents(String sourcePath, Charset charset, String lineSeparator) throws IOException {
356 FileInputStream input = null;
358 input = new FileInputStream(sourcePath);
359 return getContents(input, charset, lineSeparator);
361 uncheckedClose(input);
366 * Get the contents of a file which is located inside a given bundle.
367 * This works regardless of whether the bundle is jarred or not.
369 * @param URL the resource to read in
370 * @return the contents of the resource
371 * @throws IOException if the file is not found or other io errors occur
373 public static String getContents(URL url) throws IOException {
375 throw new IllegalArgumentException("null URL");
376 InputStream input = null;
378 input = url.openStream();
379 return FileUtils.getContents(input);
381 uncheckedClose(input);
386 * Read a binary file into a java instance. Binary file is a variant,
387 * there is a filetype in the header of the file.
388 * If requested binding is not the exact binding of the file, an adapter is tried.
391 * @param binding content binding
393 * @throws IOException
395 public static Object readFile(InputStream stream, Binding binding) throws IOException {
396 Binding datatype_binding = Bindings.getBindingUnchecked( Datatype.class );
397 Datatype type = (Datatype) Bindings.getSerializerUnchecked( datatype_binding ).deserialize( stream );
399 if (type.equals(binding.type())) {
400 return Bindings.getSerializerUnchecked( binding ).deserialize(stream);
403 Binding fileContentBinding = Bindings.getMutableBinding(type);
404 Adapter adapter = Bindings.adapterFactory.getAdapter(fileContentBinding, binding, true, false);
405 Object value = Bindings.getSerializerUnchecked( fileContentBinding ).deserialize(stream);
406 return adapter.adapt( value );
407 } catch (AdapterConstructionException e) {
408 throw new IOException(e);
409 } catch (AdaptException e) {
410 throw new IOException(e);
416 * Deletes all files and sub-directories from the specified directory. If a
417 * file is specified, only that fill will be deleted.
420 * @throws IOException
422 public static void deleteAll(File dir) throws IOException {
428 if (dir.isDirectory()) {
429 File[] fs = dir.listFiles((FileFilter) null);
434 if (f.isDirectory()) {
438 throw new IOException("Could not delete file: " + f.getAbsolutePath());
444 throw new IOException("Could not delete directory: " + dir.getAbsolutePath());
446 } else if (dir.exists()) {
448 throw new IOException("Could not delete file: " + dir.getAbsolutePath());
454 * Deletes all files and sub-directories from the specified directory. If a
455 * file is specified, only that fill will be deleted.
457 * If the given directory contains files listed in the filter those files are not deleted
460 * directory from where to start the deletion
462 * filter containing specific file paths not to delete
463 * @throws IOException
465 public static void deleteAllWithFilter(File dir, ArrayList<String> filter) throws IOException {
467 if (!filter.contains(dir.getAbsolutePath())) {
473 if (dir.isDirectory()) {
474 File[] fs = dir.listFiles((FileFilter) null);
479 if (f.isDirectory()) {
480 deleteAllWithFilter(f, filter);
482 if (!filter.contains(f.getAbsolutePath())) {
484 throw new IOException("Could not delete file: " + f.getAbsolutePath());
489 if (!filter.contains(dir.getAbsolutePath())) {
491 throw new IOException("Could not delete directory: " + dir.getAbsolutePath());
494 } else if (dir.exists()) {
495 if (!filter.contains(dir.getAbsolutePath())) {
497 throw new IOException("Could not delete file: " + dir.getAbsolutePath());
503 public static ArrayList<String> createFileFilter(File dir, ArrayList<String> filter) {
505 filter = new ArrayList<String>();
507 filter.add(dir.getAbsolutePath());
511 if (dir.isDirectory()) {
512 File[] fs = dir.listFiles((FileFilter) null);
517 if (f.isDirectory()) {
518 createFileFilter(f, filter);
520 filter.add(f.getAbsolutePath());
523 filter.add(dir.getAbsolutePath());
524 } else if (dir.exists()) {
525 filter.add(dir.getAbsolutePath());
532 * Delete a directory incl. all files and sub-directories by best effort.
533 * Does not throw exceptions if deletion fails, simply tries to delete the
534 * provided directory to the best of Java File API's abilities.
537 * directory to delete recursively
538 * @boolean <code>true</code> if all files were successfully deleted,
539 * <code>false</code> if some or all failed to be deleted
541 public static boolean deleteDir(File dir) {
542 if (LOGGER.isDebugEnabled())
543 LOGGER.debug("Deleting directory "+dir);
544 boolean result = true;
546 if (!dir.isDirectory()) return false;
547 File[] fs = dir.listFiles();
550 if (f.isDirectory()) result &= deleteDir(f);
551 if (f.isFile()) result &= f.delete();
554 boolean ok = dir.delete();
555 // if (!ok) dir.deleteOnExit();
560 public static String deleteDirs(File dir) {
561 if (LOGGER.isDebugEnabled())
562 LOGGER.debug("Deleting directory "+dir);
563 boolean result = true;
565 if (!dir.isDirectory())
566 return dir + " is not a directory!";
567 File[] fs = dir.listFiles();
570 //System.out.println("file f :" + f);
571 if (f.isDirectory()) {
572 boolean a = deleteDir(f);
576 boolean a = f.delete();
582 boolean ok = dir.delete();
583 // if (!ok) dir.deleteOnExit();
585 return "deleteDirs succesful for " + dir + " : " + result;
589 * Closes a stream and ignores any resulting exception. This is useful
590 * when doing stream cleanup in a finally block where secondary exceptions
591 * are not worth logging.
593 public static void uncheckedClose(Closeable closeable) {
595 if (closeable != null)
597 } catch (IOException e) {
603 * Extracts the specified source file in the specified bundle into the
604 * specified local directory.
606 * @param url the source URL to stream the resource from
607 * @param targetFile the target file to write the resource to
608 * @param deleteOnExit <code>true</code> to use {@link File#deleteOnExit()}
609 * on the resulting file. Note that this does not guarantee that the
610 * file is deleted when the JVM exits
611 * @return the resulting file
612 * @throws FileNotFoundException
614 public static File copyResource(URL url, File targetFile, boolean deleteOnExit) throws IOException, FileNotFoundException {
616 throw new IllegalArgumentException("null url");
618 FileOutputStream os = null;
619 InputStream is = null;
621 if (targetFile.exists())
624 is = url.openStream();
626 byte [] buffer = new byte [16384];
627 os = new FileOutputStream (targetFile);
628 while ((read = is.read (buffer)) != -1) {
629 os.write(buffer, 0, read);
634 // Request removal of the extracted files on JVM exit.
636 targetFile.deleteOnExit();
639 FileUtils.uncheckedClose(os);
640 FileUtils.uncheckedClose(is);
645 * Creates the requested location under the system's temporary directories
646 * for temporary data storage.
648 * @param pathSegments the path segments that are appended to
649 * java.io.tmpdir. The path segments musn't contain path separators.
650 * @return the resulting temporary directory as a File object
651 * @throws IOException if anything goes wrong
653 public static File getOrCreateTemporaryDirectory(boolean requireEmptyDirectory, String... pathSegments) throws IOException {
654 String separator = System.getProperty("file.separator");
655 String tempDir = System.getProperty("java.io.tmpdir");
657 throw new IllegalStateException("'java.io.tmpdir' property is not defined, is the system TMP environment variable defined?");
659 if (!hasTrailingSeparator(tempDir))
660 tempDir = tempDir + separator;
662 for (int i = 0; i < pathSegments.length-1; ++i) {
663 if (containsSeparator(pathSegments[i]))
664 throw new IllegalArgumentException("path segments contain path separators: " + Arrays.toString(pathSegments));
665 tempDir = pathSegments[i] + separator;
668 // Find name for an empty or non-existing temporary directory
669 if (pathSegments.length > 0) {
670 String lastSegment = pathSegments[pathSegments.length - 1];
673 // This loop will not exit until it succeeds or some error occurs.
675 File temp = new File(tempDir + lastSegment + suffix);
677 if (!temp.exists()) {
678 // Found non-existent temporary directory!
680 // Ok, everything seems to be fine.
683 // For some reason the directory could not be created, lets
686 // Found an existing temporary file.
687 if (temp.isDirectory() && temp.canWrite()) {
688 if (requireEmptyDirectory) {
689 String[] files = temp.list();
691 if (files.length == 0) {
692 // Ok, the directory is empty and writable.
699 // Either the directory was empty or it could not be
700 // checked. In any case, don't use the directory.
704 // Try another directory name if no success with this one.
705 suffix = Integer.toHexString(counter);
709 return new File(tempDir);
712 private static boolean containsSeparator(String s) {
713 return s.contains("/") || s.contains("\\");
716 private static boolean hasTrailingSeparator(String s) {
717 return s.endsWith("/") || s.endsWith("\\");
721 * Very simple compression using ZLib
722 * @param input the uncompressed data
723 * @return the compressed data
725 public static byte[] deflate(byte[] input) {
726 // Compress the bytes
727 Deflater compresser = new Deflater(Deflater.BEST_COMPRESSION);
728 compresser.setInput(input);
731 int bufferSize = input.length<16 ? 16 : input.length;
732 byte buffer[] = new byte[bufferSize];
735 int compressedDataLength = compresser.deflate(buffer, bufferPos, buffer.length-bufferPos);
736 bufferPos += compressedDataLength;
737 if (!compresser.finished())
738 buffer = Arrays.copyOf(buffer, buffer.length + bufferSize);
739 } while (!compresser.finished());
741 byte[] result = new byte[bufferPos+4];
742 System.arraycopy(buffer, 0, result, 4, bufferPos);
743 byte sizeData[] = LEInt.toBytes(input.length);
744 System.arraycopy(sizeData, 0, result, 0, 4);
749 * Very simple decompression using ZLib.
750 * @param input the compressed input
751 * @return the uncompressed data
752 * @throws DataFormatException
754 public static byte[] inflate(byte[] input) throws DataFormatException {
755 // Decompress the bytes
756 int inflatedSize = LEInt.toInt(input);
757 Inflater decompresser = new Inflater();
758 decompresser.setInput(input, 4, input.length - 4);
759 byte[] result = new byte[inflatedSize];
760 int resultLength = decompresser.inflate(result);
761 assert(resultLength == inflatedSize);
767 public static boolean isValidFileName(String name) {
768 File f = new File(name) ;
771 boolean ok = f.createNewFile();
775 } catch (IOException ioe) {
783 * Create a temporary directory
785 * @return temporary directory
787 public static File createTmpDir()
789 String tmp = System.getenv("tmp");
790 if (tmp==null) tmp = "c:/temp";
791 Random r = new Random();
792 String randomName = "simantics-tmp-"+(r.nextInt(10000)+10000);
793 File tmpDir = new File(tmp+"/"+randomName);
794 tmpDir.deleteOnExit();
795 Boolean ok = tmpDir.mkdirs();
796 if (!ok) throw new RuntimeException("tmp dir "+tmpDir+" was not created");
800 public static void compressZip(String sourcePath, String zipDir) throws IOException {
801 if (LOGGER.isDebugEnabled())
802 LOGGER.debug("Compressing file " + sourcePath + " to zip " + zipDir + ".");
804 File filesource = new File(sourcePath);
805 URI base = filesource.toURI();
806 Deque<File> queue = new LinkedList<File>();
807 queue.push(filesource);
808 try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(zipDir))) {
809 while (!queue.isEmpty()) {
810 filesource = queue.pop();
811 for (File child : filesource.listFiles()) {
812 String name = base.relativize(child.toURI()).getPath();
813 if (child.isDirectory()) {
815 name = name.endsWith("/") ? name : name + "/";
816 zout.putNextEntry(new ZipEntry(name));
818 zout.putNextEntry(new ZipEntry(name));
825 LOGGER.debug("Filecompression done.");
829 private static void copy(File file, ZipOutputStream zout) throws IOException {
830 try (InputStream in = new FileInputStream(file)) {
836 * Extract a zip file into a directory
840 * @throws IOException
842 public static void extractZip(File zipFile, File dst) throws IOException {
843 if (LOGGER.isDebugEnabled())
844 LOGGER.debug("Extracting zip "+zipFile);
845 try (FileInputStream fis = new FileInputStream(zipFile)) {
846 extractZip(fis, dst);
851 * Extract a zip file into a directory
854 * @param dst directory
855 * @throws IOException
857 public static void extractZip(InputStream zipInput, File dst) throws IOException {
858 byte[] buf = new byte[8192];
859 ZipInputStream zis = new ZipInputStream(zipInput);
862 entry = zis.getNextEntry();
863 while (entry != null) {
864 // for each entry to be extracted
865 String name = entry.getName();
866 if (LOGGER.isDebugEnabled())
867 LOGGER.debug("Extracting "+name);
868 File file = new File(dst, name);
870 if (entry.isDirectory())
872 if ( !file.exists() ) file.mkdirs();
874 File parent = file.getParentFile();
875 if (!parent.exists()) parent.mkdirs();
876 if (!file.exists()) file.createNewFile();
878 FileOutputStream fileoutputstream = new FileOutputStream(file);
881 while ((n = zis.read(buf, 0, buf.length)) > -1)
882 fileoutputstream.write(buf, 0, n);
883 } catch (ZipException e) {
884 throw new IOException("Failed to extract '" + name + "'.", e);
886 fileoutputstream.close();
891 entry = zis.getNextEntry();
897 // Test inflate & deflate
898 public static void main(String[] args) {
901 String str = "abcdefghijklmnopqrstuvwxyz";
902 byte data[] = str.getBytes();
903 System.out.println("Data:\t"+data.length);
904 byte deflated[] = deflate(data);
905 System.out.println("Deflated:\t"+deflated.length);
906 byte inflated[] = inflate(deflated);
907 System.out.println("Inflated:\t"+inflated.length);
908 String str2 = new String(inflated);
909 System.out.println("Strings are equal: "+str.endsWith(str2));
910 } catch (DataFormatException e) {
915 public static File[] listFilesByExtension(File directory, final String extension) {
916 return directory.listFiles(new ExtensionFilter(extension));
920 * Copy the content of the input stream into the output stream, using a temporary
921 * byte array buffer whose size is defined by {@link #IO_BUFFER_SIZE}.
923 * @param in The input stream to copy from.
924 * @param out The output stream to copy to.
926 * @throws IOException If any error occurs during the copy.
928 private static final int IO_BUFFER_SIZE = 64 * 1024;
930 public static void copy(InputStream in, OutputStream out) throws IOException {
931 byte[] b = new byte[IO_BUFFER_SIZE];
937 out.write(b, 0, read);
944 * @param maxBytesToCopy the maximum amount of bytes to copy
945 * @return the amount of bytes copied
946 * @throws IOException
948 public static long copy(InputStream in, OutputStream out, long maxBytesToCopy) throws IOException {
949 byte[] b = new byte[IO_BUFFER_SIZE];
951 while (read < maxBytesToCopy) {
952 int l = (int) Math.min((long) IO_BUFFER_SIZE, maxBytesToCopy-read);
953 int r = in.read(b, 0, l);
962 public static void delete(Path databaseLocation) throws IOException {
963 Files.walkFileTree(databaseLocation, new DeleteDirectoriesVisitor());
966 public static void copy(Path from, Path to) throws IOException {
967 Files.walkFileTree(from, new CopyDirectoriesVisitor(from, to));
970 public static class DeleteDirectoriesVisitor extends SimpleFileVisitor<Path> {
973 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
975 if (Files.exists(file))
976 throw new IOException("Could not delete file " + file.toAbsolutePath().toString());
977 return FileVisitResult.CONTINUE;
981 public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
985 if (Files.exists(dir))
986 throw new IOException("Could not delete file " + dir.toAbsolutePath().toString());
987 return FileVisitResult.CONTINUE;
991 public static class CopyDirectoriesVisitor extends SimpleFileVisitor<Path> {
993 private final Path fromPath;
994 private final Path toPath;
996 public CopyDirectoriesVisitor(Path fromPath, Path toPath) {
997 this.fromPath = fromPath;
998 this.toPath = toPath;
1002 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
1003 Path targetPath = toPath.resolve(fromPath.relativize(dir));
1004 if(!Files.exists(targetPath)){
1005 Files.createDirectory(targetPath);
1007 return FileVisitResult.CONTINUE;
1011 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
1012 Files.copy(file, toPath.resolve(fromPath.relativize(file)), StandardCopyOption.REPLACE_EXISTING);
1013 return FileVisitResult.CONTINUE;
1017 public static void syncFile(File file) throws IOException {
1018 try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {