How to get oldest file in a directory using Java - java

Are there any methods for obtaining the oldest file in a directory using java?
I have a directory i write log files to and would like to delete log files after ive recorded over 500 log files (but only want to delete the oldest ones).
The only way i could conceive myself is:
Get the list of files using the File.listFiles() method
Loop through each File
Store the last modified date using File.lastModified() and compare with File from loop iteration, keep the oldest lastModified()
The inconvenient aspect of this logic is that i would have to loop the log directory every time i want to get the oldest files and that does not seem the most efficient.
i expected the java.io.File library would have a method to get the oldest file in a directory but it doesnt seem to exist, or i havent found it. If there is a method for obtaining the oldest file in a directory or a more convenient approach in programming the solution, i would love to know.
Thanks

Based off of #Yoda comment, i figured i would answer my own question.
public static void main(String[] args) throws IOException {
File directory = new File("/logFiles");
purgeLogFiles(directory);
}
public void purgeLogFiles(File logDir){
File[] logFiles = logDir.listFiles();
long oldestDate = Long.MAX_VALUE;
File oldestFile = null;
if( logFiles != null && logFiles.length >499){
//delete oldest files after theres more than 500 log files
for(File f: logFiles){
if(f.lastModified() < oldestDate){
oldestDate = f.lastModified();
oldestFile = f;
}
}
if(oldestFile != null){
oldestFile.delete();
}
}
}

Unfortunately, you're gonna have to just walk the filesystem. Something like:
public static void main(String[] args) throws IOException {
String parentFolder = "/var/log";
int numberOfOldestFilesToFind = 5;
List<Path> oldestFiles = findOldestFiles(parentFolder, numberOfOldestFilesToFind);
System.out.println(oldestFiles);
}
private static List<Path> findOldestFiles(String parentFolder, int numberOfOldestFilesToFind) throws IOException {
Comparator<? super Path> lastModifiedComparator =
(p1, p2) -> Long.compare(p1.toFile().lastModified(),
p2.toFile().lastModified());
List<Path> oldestFiles = Collections.emptyList();
try (Stream<Path> paths = Files.walk(Paths.get(parentFolder))) {
oldestFiles = paths.filter(Files::isRegularFile)
.sorted(lastModifiedComparator)
.limit(numberOfOldestFilesToFind)
.collect(Collectors.toList());
}
return oldestFiles;
}

Related

How to list all newly created files in a specific directory?

What is the way to list files created, for example, between two timestamps? I'd like to list all newly created files and then move them to a different directory.
I'm working on Windows
public void afterDate() throws IOException {
final String pathToDirectory = "/path/to/directory";
final long afterDate = new Date().getTime();
final List<Path> paths = new ArrayList<>();
final Path directory = Paths.get(pathToDirectory);
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory)) {
for (Path path : directoryStream) {
final BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class);
final long creationTime = attr.creationTime().toMillis();
if (creationTime >= afterDate) {
paths.add(path);
}
}
}
for (final Path path : paths) {
System.out.println(path.getFileName());
}
}
If you want to actually watch out actively for newly created files, you can use a WatchService: http://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html
If you are only looking for the files that are there, you could, for example use the Apache Commons FileUtils class' methods to list all files modified/create between two specific dates.
The logic will be as below
Use a file class.
Iterate through all the files in the directory
It can be done using the file.listFiles() method.
The method will return all the files (files as well as directories) in the directory.
Then for each of the file object, get the timestamp using file.lastModified() and then check if it is between the timestamps you have specified
startTimestamp < file.lastModified() < endTimestamp

Return files values of recursive method

I would like to return values of a recursive method which list all files in directory and subdirectories. The goal is to create a HashSet of md5 file values.
For the moment the code works fine but just in root dir, not in recursive.
static Set<String> localMd5Listing() throws Exception {
List<String> localMd5List = new ArrayList<String>();
if(!baseModDirectoryFile.exists()){
System.out.println("baseModDirectory doesn't exist, check your baseModDirectory variable.");
}
else{
if(baseModDirectoryFile.isDirectory()){
File[] paths = baseModDirectoryFile.listFiles();
if(paths != null){
for(File path:paths){
if(path.isFile()){
FileInputStream fis = new FileInputStream(path);
String md5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(fis);
fis.close();
localMd5List.add(md5);
}
else if(path.isDirectory()){
listChildren(path);
//Check md5 for children files as path.isFile condition
}
}
}
}
}
Set<String> localSet = new HashSet<String>();
localSet.addAll(localMd5List);
localMd5List.clear();
localMd5List.addAll(localSet);
return localSet;
}
listChildren method for recursive result :
public static void listChildren(File dir) throws IOException{
File[] files = dir.listFiles();
for(File file:files){
if(file.isDirectory()){
listChildren(file);
}
else{
//Return file variable to use them in localMd5Listing()
}
}
}
Unfortuntely I didn't know how to link the 2 methods to return values of listChildren() in localMd5Listing(). I think it's not the good way to have a listFile() in the first method too.
Thank you !
Extract the if statement in localMD5listing into a method recurseMD5 that takes a File argument and the list of hashes for update. Then start off the process by calling
recurseMD5(baseModDirectoryFile, localmd5List);
and in recurseMD5 you just recurse on all listFiles() when the parameter is a directory. If, OTOH, it is a regular file, you add the md5.
void recurseMD5(File it, List<String> hashes) {
if (it.isDirectory) {
for (File f : it.listFiles()) recurseMD5(f, hahses);
}
else {
// process MD5 hash of file
}
}
the basic setup you want is something like the following pseudocode
public List<string> getAllHashes() {
List<String> hashes = new List<String>();
//calculate all the hashes
foreach(directory d in directories) {
hashes.add(directory.getAllHashes());
}
return hashes;
}
I know that this is no full code whatsoever, but with this you should be able to make the recursive loop. Don't forget to check if there actually ARE directories in there, or you might get a nullpointer!

How can move file from one directory to another

I am sorry,I modified my entire question.
I want to move some specific files(based on fileExtension) from one directory to another.
For this, I plan to write 2 functions names are ListOfFileNames and MovingFiles.
If you pass directorypath and filetype as a arguments toListOfFileNames(),it returns List<File>data.this list have entire file path.
MovingFiles() move files from source Directory to destination.
I tried MovingFiles function, in the following way.It's not working.
public void MovingFiles(String SourcePath,String directoryPath,String fileType) throws IOException
{
ExternalFileExecutions ExternalFileExecutionsObject=new ExternalFileExecutions();
List<File> fileNames=ExternalFileExecutionsObject.ListOfFileNames(SourcePath, fileType);
for (int fileIndex = 0; fileIndex < fileNames.size(); fileIndex++)
{
fileNames[fileIndex].renameTo(new File(directoryPath+"/" + fileNames[fileIndex].getName()))
}
}
I think, I need to convert List<File> to List<String>.that's why Previously I asked like that.
#tbodt replyed for my question.I failed to integrate his answer in my function.so I modified my question.
Again sorry, for modifying my entire question.
Thanks.
If you want to get a List<String> with each element the canonical path of the corresponding file, this is how:
List<String> list = new ArrayList<String>();
for (File file : finalListNames)
list.add(file.getCanonicalPath());
If that's not what you want, please clarify.
If understand you, using guava, I would write it in this way:
public static List<File> listOfFiles(){
return Lists.newArrayList(new File("source.txt"), new File("test.txt"));
}
public static List<String> listOfFileNames(){
return Lists.transform(listOfFiles(), new Function<File, String>() {
public String apply(File input) {
return input.getAbsolutePath();
}
});
}
You cant convert it. But what you can is that you can retrieve the name (what ever it is) while iterating through File list. A very handy solution would be LINQ, and select
This my answer. I used #tbodt code in my code.
public void MovingFiles(String SourcePath,String directoryPath,String fileType) throws IOException
{
ExternalFileExecutions ExternalFileExecutionsObject=new ExternalFileExecutions();
List<File> fileNames=ExternalFileExecutionsObject.ListOfFileNames(SourcePath, fileType);
List<String> list = new ArrayList<String>();
for (File file : fileNames)
list.add(file.getCanonicalPath());
for (int fileIndex = 0; fileIndex < list.size(); fileIndex++)
{
File file=new File(list.get(fileIndex));
file.renameTo(new File(directoryPath+"/",file.getName()));
}
}
you move a file from one directory to another directory with the same file name
try{
File afile =new File("C:\\folder1\\file1.txt");
if(afile.renameTo(new File("C:\\folder2\\" + afile.getName()))){
System.out.println("File is moved successful!");
}else{
System.out.println("File is failed to move!");
}
}catch(Exception e){
e.printStackTrace();
}

Odd Bug , using File.RenameTo()

I have a program in which i must rename a set of folders. they are all in "ID [Name]" format, and I want to rename them to "Name [ID]". (Its more of a training for me, for learning java))
the problem is, if the number of folders it must rename go beyond 20-24 . the program won't work, and will give the files faulty names. (the renaming process succeeds, but names are wrong)
but if they are below 20 folders, it works perfectly.(tested with the same folders)
here's the whole code:
public class DirRename {
private String parentDir;
private DirectoryStream<Path> fileList;
public DirRename(final Path dir)
{
parentDir = dir.toString();
if(!Files.exists(dir) || !Files.isDirectory(dir) || !Files.isReadable(dir))
System.out.println("Directory Read Error!!!");
//filter to return only directories in parent folder
DirectoryStream.Filter<Path> dirOnlyFilter =
new DirectoryStream.Filter<Path>() {
public boolean accept(Path file) throws IOException {
return (Files.isDirectory(file));
}
};
try
{
fileList = Files.newDirectoryStream(dir,dirOnlyFilter);
}
catch(IOException | DirectoryIteratorException x)
{
System.err.println(x);
}
}
public void rename()
{
for(Path filepath : fileList)
{
String name = filepath.getFileName().toString();
File inFile = filepath.toFile();
if(!inFile.exists() || !inFile.isDirectory() || !inFile.canWrite())
{
System.out.println("Directory is not writeable");
return;
}
Pattern regex = Pattern.compile("((?:[\\w\\d]*(?:\\s|-){0,2}[\\w\\d]+)*)\\s*-*\\s*(?:\\[|\\Q(\\E)(.+)(?:\\]|\\Q)\\E)$");
Matcher match = regex.matcher(name);
while(match.find())
{
String gameID = match.group(1);
String gameName = match.group(2);
String rename = parentDir+File.separator+gameName+" ["+gameID+"]";
File toFile = new File(rename);
if(!Paths.get(rename).isAbsolute())
{
System.out.println("Cannot rename "+name+"to "+rename);
return;
}
if(inFile.renameTo(toFile))
System.out.println("Success!");
else
System.out.println("Renaming Failed!!! for "+rename);
}
}
}
}
I tried checking the names with "system.out.println(toFile.getName())" while deleting the line "inFile.renameTo(toFile)". all names were correct.
but when i added that line back, the same names were printed incorrectly.(although some that were printed correctly were renamed incorrectly)
I'm completely confused. and I'm new to java, and generally less than a noob programmer. can someone please tell me what's going on?
Many thanks
EDIT: I found the problem.the loop:
for(Path filepath : fileList){}
runs 116 times while i only have 64 folders. I can't find any explanation as to why this happens, I use the same loop to print folder names only in the following function and it runs exactly 64 times.( exactly the number of folders I have)
public void printFolders()
{
for(Path filepath : fileList)
System.out.println(filepath.getFileName());
}
okay I finally Fixed my own problem. here's my guess on why this happened (I don't know the inner working of DirectoryStream so its just a guess).
when the folders were more than a few, the stream would read the previously renamed folders and add them as new folders, thus they were getting renamed twice. either changing the name back to original, or deforming it (the renaming wasn't designed to be 100% re-applicable).
In case of a few folders, the loop would be over before the stream had the chance to refresh, thus no problems.
so here's how i fixed it. by adding the following method, and iterating through an array of paths instead of the stream.
private Path[] getVerifiedPaths()
{
ArrayList<Path> verifiedFilePaths= new ArrayList<>();
for(Path filepath : fileList)
verifiedFilePaths.add(filepath);
return verifiedFilePaths.toArray(new Path[0]);
}
Path[] filePaths = getVerifiedPaths();
for(Path filePath : filePaths) { ...rename...}
instead of:
for(Path filepath : fileList){...rename...}
thanks to "JB Nizet" for his suggestion (comment above).

Java to traverse all directories and find a file

I have to search for a file which can be in any directory or drive. It should be compatible with any operating system. When I googled, most of the code iterates through a particular directory but not full file system. Is there any way to do it efficiently ? Any help or suggestion will be really appreciated.
Below code where i got from http://www.mkyong.com/java/how-to-traverse-a-directory-structure-in-java/, but we have to pass some directory as parameter. Is there a way to generalize to get all the locations ?
public static void main (String args[]) {
displayIt(new File("C:\\"));
}
public static void displayIt(File node){
System.out.println(node.getAbsoluteFile());
if(node.isDirectory()){
String[] subNote = node.list();
for(String filename : subNote){
displayIt(new File(node, filename));
}
}
Apache Commons-IO is a good API for this kind of Operation. For Unix system you could just use root "/" however this will not do for windows, hence you will have to ask for all roots and iterate over them:
File[] roots = File.listRoots();
Collection<File> files = new ArrayList<File>();
for(File root : roots) {
files.addAll(FileUtils.listFiles(
root,
new RegexFileFilter(<your regex filter>),
DirectoryFileFilter.DIRECTORY
));
}
This sort of code snippet will list all the files in a directory and sub directories. You do not have to add any of the files to allFiles and you could do your check there. As you havent supplied any code yet (so I assume you havent tried anything) I'll let you update it ;)
private void addFiles(File file, Collection<File> allFiles) {
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
allFiles.add(f);
addFiles(f, allFiles);
}
}
}
if you want to do this by recursion, here is code for DFS, code might not working (i never test it), and it is not optimized, but it might give you some ideas how to solve your problem
File find(String directoryName, String pattern)
{
File currentDirectory = loadFile(directoryName);
for (String name: currentDirectory .list())
{
File children = loadFile(name)
if (children.isDirectory())
{
File file = find(name, pattern)
if (file !=null)
{
return file;
}
}
else
{
if (match(name,pattern)
{
return children;
}
}
}
return null;
}

Categories

Resources