Recursive File Updating - java

I have a method which recursively copies files from one folder to another effectively creating a 1:1 copy. Another twist it should do is only copying files that don't exist at their destination and files which have been modified. Here's code I wrote for this:
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
public static void copy(File source, File destination) throws IOException
{
copy(source, destination, true);
}
private static void copy(File source, File destination, boolean onlyUpdates)
throws IOException
{
if (source.isDirectory())
{
if (!destination.exists())
{
createFolder(destination);
}
String[] sourceChildren = source.list();
for (int sourceChildrenIndex = 0; sourceChildrenIndex < sourceChildren.length; sourceChildrenIndex++)
{
File currentSource = new File(source,
sourceChildren[sourceChildrenIndex]);
File currentDestination = new File(destination,
sourceChildren[sourceChildrenIndex]);
copy(currentSource, currentDestination, onlyUpdates);
}
} else
{
if (onlyUpdates)
{
if (isNewer(source, destination) && isDifferent(source, destination))
{
copyFile(source, destination);
}
}
}
}
private static boolean isDifferent(File source, File destination)
{
return source.length() != destination.length();
}
private static boolean isNewer(File source, File destination)
{
return source.lastModified() > destination.lastModified();
}
private static void copyFile(File source, File destination)
throws IOException
{
try
{
Files.copy(source.toPath(), destination.toPath(),
StandardCopyOption.REPLACE_EXISTING);
} catch (FileSystemException e)
{
e.printStackTrace();
}
}
private static void createFolder(File destination)
{
destination.mkdir();
}
Now my question is why it doesn't always update the destination folder fully. When I abort the process and start it again later it won't copy some subfolders and files over to the destination. Do you see anything wrong with the code?

You need to compare the sizes of the files and not only their timestamps. If your program aborts midflight a partial copy will be left behind.

Related

Java copy files matching pattern from many folders to another folder

Please take a look at the code I have so far and if possible explain what I'm doing wrong. I'm trying to learn.
I made a little program to search for a type of file in a directory and all its sub-directories and copy them into another folder.
Code
import java.util.ArrayList;
import java.util.List;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
public class FandFandLoop {
public static void main(String[] args) {
final File folder = new File("C:/Users/ina/src");
List<String> result = new ArrayList<>();
search(".*\\.txt", folder, result);
File to = new File("C:/Users/ina/dest");
for (String s : result) {
System.out.println(s);
File from = new File(s);
try {
copyDir(from.toPath(), to.toPath());
System.out.println("done");
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
public static void copyDir(Path src, Path dest) throws IOException {
Files.walk(src)
.forEach(source -> {
try {
Files.copy(source, dest.resolve(src.relativize(source)),
StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
});
}
public static void search(final String pattern, final File folder, List<String> result) {
for (final File f : folder.listFiles()) {
if (f.isDirectory()) {
search(pattern, f, result);
}
if (f.isFile()) {
if (f.getName().matches(pattern)) {
result.add(f.getAbsolutePath());
}
}
}
}
}
It works, but what it actually does is to take my .txt files and write them into another file named dest without extension.
And at some point, it deletes the folder dest.
The deletion happens because of StandardCopyOption.REPLACE_EXISTING, if I understand this, but what I would have liked to obtain was that if several files had the same name then only one copy of it should be kept.
There is no need to call Files.walk on the matched source files.
You can improve this code by switching completely to using java.nio.file.Path and not mixing string paths and File objects. Additionally instead of calling File.listFiles() recursively you can use Files.walk or even better Files.find.
So you could instead use the following:
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Objects;
import java.util.function.BiPredicate;
import java.util.stream.Stream;
public class CopyFiles {
public static void copyFiles(Path src, Path dest, PathMatcher matcher, CopyOption... copyOptions) throws IOException {
// Argument validation
if (!Files.isDirectory(src)) {
throw new IllegalArgumentException("Source '" + src + "' is not a directory");
}
if (!Files.isDirectory(dest)) {
throw new IllegalArgumentException("Destination '" + dest + "' is not a directory");
}
Objects.requireNonNull(matcher);
Objects.requireNonNull(copyOptions);
BiPredicate<Path, BasicFileAttributes> filter = (path, attributes) -> attributes.isRegularFile() && matcher.matches(path);
// Use try-with-resources to close stream as soon as it is not longer needed
try (Stream<Path> files = Files.find(src, Integer.MAX_VALUE, filter)) {
files.forEach(file -> {
Path destFile = dest.resolve(src.relativize(file));
try {
copyFile(file, destFile, copyOptions);
}
// Stream methods do not allow checked exceptions, have to wrap it
catch (IOException ioException) {
throw new UncheckedIOException(ioException);
}
});
}
// Wrap UncheckedIOException; cannot unwrap it to get actual IOException
// because then information about the location where the exception was wrapped
// will get lost, see Files.find doc
catch (UncheckedIOException uncheckedIoException) {
throw new IOException(uncheckedIoException);
}
}
private static void copyFile(Path srcFile, Path destFile, CopyOption... copyOptions) throws IOException {
Path destParent = destFile.getParent();
// Parent might be null if dest is empty path
if (destParent != null) {
// Create parent directories before copying file
Files.createDirectories(destParent);
}
Files.copy(srcFile, destFile, copyOptions);
}
public static void main(String[] args) throws IOException {
Path srcDir = Paths.get("path/to/src");
Path destDir = Paths.get("path/to/dest");
// Could also use FileSystem.getPathMatcher
PathMatcher matcher = file -> file.getFileName().toString().endsWith(".txt");
copyFiles(srcDir, destDir, matcher);
}
}

Update : Move files from one folder to another based on file extension

Situation:
I am doing an automation where I have to download only CSV files from a set of files. And now i want to move only CSV files from one folder to another.
Question:
Can you please provide me code to delete files from the source folder as soon as the files are moved?
This is my code so far:
public class MyFilteredFileList {
public static void main(String a[])
{
try {
File source = new File("C:\\Users\\sh370472\\Downloads");
File dest = new File("E:\\Query\\");
FileUtils.copyDirectory(source, dest, new FileFilter() {
#Override
public boolean accept(File pathname)
{
boolean source=pathname.getName().toLowerCase().endsWith(".csv");
if (source)
return true;
return false;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
Edit : But I am still facing a problem. It's downloading all the files at once and then deleting. But my requirement is something like - It should download first file -> move the downloaded file into another folder -> delete first downloaded file from download folder -> download second folder... and the process repeats
Just add pathname.deleteOnExit(); on accept method
#Override
public boolean accept(File pathname) {
boolean source = pathname.getName().toLowerCase().endsWith(".csv");
if (source) {
pathname.deleteOnExit();
return true;
}
return false;
}
whole code :
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
/**
* Created by Lenovo on 02/12/2018.
*/
public class FileMove {
public static void main(String a[])
{
try {
File source = new File("C:\\Users\\sh370472\\Downloads");
File dest = new File("E:\\Query\\");
FileUtils.copyDirectory(source, dest, new FileFilter() {
#Override
public boolean accept(File pathname) {
boolean source = pathname.getName().toLowerCase().endsWith(".csv");
if (source) {
pathname.deleteOnExit();
return true;
}
return false;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
You can simply move the file this way.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class MyFilteredFileList {
public static void main(String a[]){
try {
Files.move (Paths.get("C:\\Users\\sh370472\\Downloads\\file.txt"),
Paths.get("E:\\Query\\file.txt"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Copy this class and run :) Try setting the correct source and destination path if it doesn't work.
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
public class MoveFile {
private boolean moveFile(String source, String destination) throws IOException {
Path srcPath = Paths.get(source);
Path destPath = Paths.get(destination);
System.out.println("Moving processed file to archive");
Files.move(srcPath, destPath, StandardCopyOption.REPLACE_EXISTING);
return true;
}
private File[] getTheFilesWithExtension(String filePath, String ext) {
File fileDir = new File(filePath);
File[] files = fileDir.listFiles((dir, name) -> name.toLowerCase().endsWith(ext));
return files;
}
public static void main(String... args){
{
MoveFile moveFile = new MoveFile();
String sourcePath = "C:\\\\Users\\\\sh370472\\\\Downloads";
String destPath = "E:\\Query\\";
String extension = ".csv";
try {
File[] files = moveFile.getTheFilesWithExtension(sourcePath, extension);
for (File file : files){
moveFile.moveFile(file.getPath(),destPath+file.getName());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Note: Its for Java 8 and above , for lower java version, you can update the lambda expression in getTheFilesWithExtension with the below code
private File[] getTheFilesWithExtension(String filePath, String ext) {
File fileDir = new File(filePath);
File[] files = fileDir.listFiles(
new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(ext);
}
}
);
return files;
}

How can I apply read only attribute to all .docx and .pptx files in the C: directory? Using java

Please I am trying to apply setReadOnly() function to all documents having the .docx and .pptx file extension in my C: drive. As in the readonly show apply to any document that has the file extension in C: directory.
Please someone show assist me on how to implement that. This is what I have able to come with but I don’t think is the correct way nor the correct code
import java.io.File;
import java.io.IOException;
public class FileReadAttribute {
public static void main(String[] args) throws IOException {
File file = new File("C:*.docx");
file.setReadOnly();
if(file.canWrite()) {
System.out.println("This file is writable");
} else {
System.out.println("This file is read only");
}
}
}
try this:
import java.io.File;
import java.io.IOException;
public class FileReadAttribute {
public static void main(String[] args) throws IOException {
File c_drive = new File("C:\\");
setPermission(c_drive);
}
private static void setPermission(File parent) {
File[] files = parent.listFiles();//get all files of this directory
for (File file : files) {
if (file.isDirectory()) {
setPermission(file); //set permission of the files in this directory
} else {
String fileExtension = getFileExtension(file);
if (fileExtension.equals("docx") || fileExtension.equals("pptx")) {
file.setReadOnly();
}
}
}
}
private static String getFileExtension(File file) {
String name = file.getName();
return name.substring(name.lastIndexOf(".") + 1);
}
}
You can use FileNameFilter. This should work
File dir = new File("/users/blah/dirname");
File[] files = dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".docx");
}
});
if (files != null) {
Stream.of(files).forEach(File::setReadOnly);
}
If you are using Java less than 8. Replace Stream.of with traditional for loop.

Trying to Open eac subfolder of a directory and copying pdf files to different common folder

How can I go to each subfolder in a given directory and take a pdf file from there like wise collect all pdf from all subfolders and move to a common folder?.
I've tried below code which is failing.
import java.io.File;
import java.io.IOException;`enter code here`
import java.nio.file.Files;
import java.util.ArrayList;
public class PDFMover{
/**
* #param args
* #return
* #throws IOException
*/
public static void main(String[] args) throws IOException {
File mainfolder = new File("C:\\Users\\myuser\\Desktop\\X\\Report");
File[] fulllist = mainfolder.listFiles();
ArrayList<String> listofpath = new ArrayList<String>(fulllist.length);
for (File x : fulllist)
{
if (x.isDirectory())
{
System.out.println("Directory:" +x.getName());
}
else
{
System.out.println("Notfound");
}
}
String source = "C:\\Users\\myuser\\Desktop\\X\\Report";
String destination ="C:\\Users\\myuser\\Desktop\\Destination";
File f1 = new File(source);
File f2 = new File(destination);
for (File x : fulllist)
{
if(mainfolder.isDirectory())
{
Files.copy(f1.toPath(), f2.toPath());
}
}
}
}
If you use nio.Files the solution to find every file in every subdirectory would be:
import java.nio.file.FileSystems;
import java.nio.file.Files;
public class FileWalker
{
public static final String PATH = "C:\\Users\\myuser\\Desktop\\X\\Report";
public static void main(final String[] args) throws Exception
{
Files.walk(FileSystems.getDefault()
.getPath((PATH)))
.map(path -> path.getFileName())
.filter(name -> name.toString()
.toLowerCase()
.endsWith("pdf"))
.forEach(System.out::println);
}
}
Instead of System.out.println you have to copy the files.
You can either use a recursive function, or use an explicit stack to do this. You simply check each of the files to see if they match your selection criteria. If not and if it's a directory you put it in the stack and move on.
Here's some working code using this approach. It let's you find files with a particular extension down to a particular recurs depth. The getRecurseFiles() returns a list of canonical path strings.
import java.util.*;
import java.io.*;
class Tracker {
File[] list;
int depth;
public Tracker (File[] l, int d) {
this.list = l;
this.depth = d;
}
}
public class RecTraverse {
public static int MAX_DEPTH = 10;
List<String> getRecurseFiles (String dir) throws IOException {
List<String> requiredFiles = new ArrayList<>();
Stack<Tracker> rstack = new Stack<>();
File[] list = new File(dir).listFiles();
rstack.push(new Tracker(list, 1));
while (!rstack.empty()) {
Tracker tmp = rstack.pop();
for (int i=0; i<tmp.list.length; i++) {
File thisEntry = tmp.list[i];
String fileName = tmp.list[i].getCanonicalPath();
if (thisEntry.isFile()) {
int j = fileName.lastIndexOf('.');
if (j > 0) {
String extension = fileName.substring(j+1).toLowerCase();
if (extension.equals("pdf"))
requiredFiles.add(tmp.list[i].getCanonicalPath());
}
} else {
System.out.println(thisEntry.getCanonicalPath());
File[] thisEntryList = thisEntry.listFiles();
if (thisEntryList != null && tmp.depth+1 <= MAX_DEPTH) {
rstack.push(new Tracker(thisEntryList, tmp.depth+1));
}
}
}
}
return requiredFiles;
}
public static void main (String[] arg) {
try {
List<String> l = new RecTraverse().getRecurseFiles(".");
for (String s : l)
System.out.println(s);
} catch (IOException e) {
}
}
}

List pdf files recursively

I've created a tool that checks pdf files for errors. The tool selects with a filechooser a directory, checks if there are pdf files and scans them. But I want that the tool checks recursively the directory. If I try this code:
public class RecursiveFileDisplay {
public static void main(String[] args) {
File currentDir = new File("C://Users//Tommy//Desktop"); // current directory
displayDirectoryContents(currentDir);
}
public static void displayDirectoryContents(File dir) {
try {
File[] files = dir.listFiles();
for (File file : files) {
if (file.isDirectory()) {
displayDirectoryContents(file);
} else {
if (file.getName().endsWith((".pdf"))) {
System.out.println(" file:" + file.getCanonicalPath());
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
the tool will show me for each directory on a Desktop a new list and doesn't sum up all the results in one list...:
If you want your program to create a whole list with all the files processed, then you have to modify your displayDirectoryContent method as following:
public static List<File> displayDirectoryContents(File dir) {
ArrayList<File> rtnFiles = new ArrayList<File>();
try {
File[] files = dir.listFiles();
for (File file : files) {
if (file.isDirectory()) {
rtnFiles.addAll(displayDirectoryContents(file));
} else {
if (file.getName().endsWith((".pdf"))) {
System.out.println(" file:" + file.getCanonicalPath());
rtnFiles.add(file);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return rtnFiles;
}
This way, it will return you with all the files found/processed. If you want this method only return you with failed files, then you will have to check not only the file is a .pdf file, but it is ok/failed, and then add to the list.
If you want to controll sum, then you can create a Java Class with two Integers one for pdf found files, and other for pdf failed found files. Recursively you can cum it up in the same way I build the list.
Hope it helps.
You can use the java.nio.file.Files.walkFileTree
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
public class FolderRecurs {
public Path pathToStart = Paths.get("The_URI_OF_THE_ROOT_DIRECTORY");
public List<File> listPDF = new ArrayList<>();
public FolderRecurs(Path pathToStart) {
this.pathToStart = pathToStart;
}
public void goRecurs() throws IOException{
Files.walkFileTree(pathToStart, new SimpleFileVisitor<Path>() {
//for folders
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs){
boolean error = false;
//your code
return error?FileVisitResult.TERMINATE : FileVisitResult.CONTINUE;
}
//for files
#Override
public FileVisitResult visitFile(Path filePath, BasicFileAttributes attrs){
boolean error = false;
//Convert path to file
File file = filePath.toFile();
if (file.getName().endsWith((".pdf"))){
listPDF.add(file);
}
//your code
return error?FileVisitResult.TERMINATE : FileVisitResult.CONTINUE;
}
});
}
}

Categories

Resources