Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 days ago.
Improve this question
I have the following csv file structure:
car
score
description
Opel
30
43
Volvo
500
434
Kia
50
3
Toyota
4
4
Mazda
5000
4
I want to find numbers match. For example if 3 numbers are found : 50, 500, 5000 I want to find this pattern. I tried this:
File filesList[] = directoryPath.listFiles(textFileFilter);
System.out.println("List of the text files in the specified directory:");
for(File file : filesList) {
try {
try (var br = new FileReader(file.getAbsolutePath(), StandardCharsets.UTF_16)){
List<CsvLine> beans = new CsvToBeanBuilder(br)
.withType(CsvLine.class)
.build()
.parse();
Path originalPath = null;
boolean found50 = false;
boolean found500 = false;
boolean found5000 = false;
for (CsvLine item : beans)
{
originalPath = file.toPath();
if (item.getAvgMonthlySearches() != null)
{
if (item.getValue().compareTo(BigDecimal.valueOf(50)) == 0)
{
found50 = true;
}
if (item.getValue().compareTo(BigDecimal.valueOf(500)) == 0)
{
found500 = true;
}
if (item.getValue().compareTo(BigDecimal.valueOf(5000)) == 0)
{
found5000 = true;
}
}
if(found50 == true && found500 == true && found5000 == true){
found50 = false;
found500 = false;
found5000 = false;
// Move here file into new subdirectory when file is invalid
Path copied = Paths.get(file.getParent() + "/invalid_files");
try {
// Use resolve method to keep the "processed" as folder
br.close();
Files.move(originalPath, copied.resolve(originalPath.getFileName()), StandardCopyOption.REPLACE_EXISTING);
break;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
if (file.exists())
{
// Move here file into new subdirectory when file processing is finished
Path copied = Paths.get(file.getParent() + "/processed");
try {
// Use resolve method to keep the "processed" as folder
br.close();
Files.move(originalPath, copied.resolve(originalPath.getFileName()), StandardCopyOption.REPLACE_EXISTING);
break;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
} catch (Exception e){
e.printStackTrace();
}
Path originalPath = file.toPath();
System.out.println(String.format("\nProcessed file : %s, moving the file to subfolder /processed\n",
originalPath));
}
#Getter
#Setter
public class CsvLine {
#CsvBindByPosition(position = 2)
private BigDecimal value;
}
I want if I match all values 50, 500 and 5000 on csv file lines to move the current file into a separate folder. I tried to run the code but nothing happens. Do you know where I'm wrong?
Here is a working example with more structured code according your needs.
Exception handling is ommitted since I have no idea about your requirements regarding to them, but you can easily adapt this code for your needs.
Also I have no idea what is this item.getAvgMonthlySearches(), since I don't see this field in your csv example. This field and checking of it also ommitted, but you can add it.
The code is decomposed to:
Main method with preparations and starting the entire directory processing
method for directory processing
method for file processing
method for line processing
method for moving the file
factory method for creating predicates ("matchers" or "conditions" that you can specify as much as you want, instead of using "found500" or "found5000" etc.)
I used this csv according to your question:
car;score;description
Opel;30;43
Volvo;500;434
Kia;50;3
Toyota;4;4
Mazda;5000;4
So, the model is the following:
#Data
public class CsvLine {
#CsvBindByPosition(position = 1)
private BigDecimal value;
}
The main method:
public static void main(String[] args) throws IOException {
// preparing
File directory = Path.of("/Users/kerbermeister/csv/").toFile();
// we want to process only csv files from directory
FilenameFilter csvFilter = (dir, name) -> name.toLowerCase().endsWith(".csv");
// specify what conditions should meet the value of every line
// to be considered as a valid line
List<Predicate<BigDecimal>> fileMatchConditions = List.of(
ne(new BigDecimal("50")),
ne(new BigDecimal("500")),
ne(new BigDecimal("5000"))
);
// start directory processing
processDirectory(directory, csvFilter, fileMatchConditions);
}
The primary process with the rest methods:
public static void processDirectory(File directory, FilenameFilter filter,
List<Predicate<BigDecimal>> fileMatchConditions) throws IOException {
Path processedFolderPath = createDirectory(Path.of(directory.getAbsolutePath() + "/processed"));
Path invalidFilesFolderPath = createDirectory(Path.of(directory.getAbsolutePath() + "/invalid_files"));
File[] files = directory.listFiles(filter);
if (Objects.nonNull(files)) {
for (File file : files) {
if (isFileValid(file, fileMatchConditions)) {
// if file is valid, then move it to the processed directory
moveFile(file, processedFolderPath, StandardCopyOption.REPLACE_EXISTING);
} else {
// if file is not, then move it to the invalid-files directory
moveFile(file, invalidFilesFolderPath, StandardCopyOption.REPLACE_EXISTING);
}
}
}
}
/**
* primary file validation
* parses the file
*/
public static boolean isFileValid(File file, List<Predicate<BigDecimal>> matchConditions) throws IOException {
try (Reader reader = Files.newBufferedReader(file.getAbsoluteFile().toPath())) {
List<CsvLine> lineBeans = new CsvToBeanBuilder<CsvLine>(reader)
.withType(CsvLine.class)
.withSkipLines(1) // skip header
.withSeparator(';') // line separator
.build()
.parse();
// unique violations found within this file
Set<BigDecimal> violations = new HashSet<>();
for (CsvLine line : lineBeans) {
// skip if value in the line is null
if (Objects.isNull(line.getValue())) {
continue;
}
if (!isLineValid(line, matchConditions)) {
violations.add(line.getValue());
}
// if we reached all the violations, then file is not valid
if (violations.size() == matchConditions.size()) {
return false;
}
}
}
return true;
}
/**
* if all predicates return true, then line is valid
*/
public static boolean isLineValid(CsvLine line, List<Predicate<BigDecimal>> conditions) {
return conditions.stream().allMatch(predicate -> predicate.test(line.getValue()));
}
/**
* move the file to the directory specified
*/
public static void moveFile(File file, Path moveTo, StandardCopyOption option) throws IOException {
Files.move(file.toPath(), moveTo.resolve(file.getName()), option);
}
/**
* factory method for Predicate that returns true if compareTo != 0
*/
public static <T extends Comparable<T>> Predicate<T> ne(T target) {
return (value) -> value.compareTo(target) != 0;
}
public static Path createDirectory(Path path) throws IOException {
if (!Files.exists(path) || !Files.isDirectory(path)) {
return Files.createDirectory(path);
}
return path;
}
So, I think you can adapt it to your needs.
Related
I have a project structure like below:
Now, my problem statement is I have to iterate resources folder, and given a key, I have to find that specific folder and its files.
For that, I have written a below code with the recursive approach but I am not getting the output as intended:
public class ConfigFileReader {
public static void main(String[] args) throws Exception {
System.out.println("Print L");
String path = "C:\\...\\ConfigFileReader\\src\\resources\\";
//FileReader reader = new FileReader(path + "\\Encounter\\Encounter.properties");
//Properties p = new Properties();
//p.load(reader);
File[] files = new File(path).listFiles();
String resourceType = "Encounter";
System.out.println(navigateDirectoriesAndFindTheFile(resourceType, files));
}
public static String navigateDirectoriesAndFindTheFile(String inputResourceString, File[] files) {
String entirePathOfTheIntendedFile = "";
for (File file : files) {
if (file.isDirectory()) {
navigateDirectoriesAndFindTheFile(inputResourceString, file.listFiles());
System.out.println("Directory: " + file.getName());
if (file.getName().startsWith(inputResourceString)) {
entirePathOfTheIntendedFile = file.getPath();
}
} else {
System.out.print("Inside...");
entirePathOfTheIntendedFile = file.getPath();
}
}
return entirePathOfTheIntendedFile;
}
}
Output:
The output should return C:\....\Encounter\Encounter.properties as the path.
First of all, if it finds the string while traversing it should return the file inside that folder and without navigating the further part as well as what is the best way to iterate over suppose 1k files because every time I can't follow this method because it doesn't seem an effective way of doing it. So, how can I use an in-memory approach for this problem? Please guide me through it.
You will need to check the output of recursive call and pass that back when a match is found.
Always use File or Path to handle filenames.
Assuming that I've understood the logic of the search, try this which scans for files of form XXX\XXXyyyy
public class ConfigReader
{
public static void main(String[] args) throws Exception {
System.out.println("Print L");
File path = new File(args[0]).getAbsoluteFile();
String resourceType = "Encounter";
System.out.println(navigateDirectoriesAndFindTheFile(resourceType, path));
}
public static File navigateDirectoriesAndFindTheFile(String inputResourceString, File path) {
File[] files = path.listFiles();
File found = null;
for (int i = 0; found == null && files != null && i < files.length; i++) {
File file = files[i];
if (file.isDirectory()) {
found = navigateDirectoriesAndFindTheFile(inputResourceString, file);
} else if (file.getName().startsWith(inputResourceString) && file.getParentFile().getName().equals(inputResourceString)) {
found = file;
}
}
return found;
}
}
If this is slow especially for 1K of files re-write with Files.walkFileTree which would be much faster than File.list() in recursion.
I need to find a file name from the list of filenames and to initiate two methods according to the found result. I tried:
FileList result = service.files().list()
.setPageSize(10)
.setFields("nextPageToken, files(id, name)")
.execute();
List<File> files = result.getFiles();
if (files == null || files.size() == 0) {
System.out.println("No files found.");
} else {
System.out.println("Files:");
for (File file : files) {
System.out.printf("%s (%s)\n", file.getName(), file.getId());
Boolean found = files.contains("XYZ");
if(found)
{
insertIntoFolder();
} else {
createFolder();
}
}
}
I need to find XYZ (the filename) from a list of file names (like sjh, jsdhf, XYZ, ASDF). Once I've found it I need to stop the search. If the name doesn't match the list of names I need to create a folder only once after checking all names from that list.
Boolean found = files.contains("XYZ");
This line is problematic. files is a list of File objects, none of which will match the String "XYX". List.contains() essentially calls Object.equals() on every element of the list, and File.equals("XYZ") will always return false.
If you're programming in an IDE like Eclipse it should show a warning on this line, since it's a bug that can be detected at compile-time.
To determine if a File in a List<File> has a filename matching a given string you need to operate on the filename itself, so the above line should instead be:
boolean found = file.getName().equals("XYZ");
Depending on what exactly you're trying to match you might want to use .getName(), .getAbsolutePath(), or .toString().
It's also a good idea to use the Path API introduced in Java 7, rather than File, which is essentially a legacy class at this point.
If you want a more elegant solution than manually looping over files looking for a match you can use Files.newDirectoryStream(Path, Filter) which allows you to define a Filter predicate that only matches certain files, e.g.
Files.newDirectoryStream(myDirectory, p -> p.getFileName().toString().equals("XYZ"))
File.list(FilenameFilter) is a similar feature for working with File objects, but again, prefer to use the Path API if possible.
Here is a example:
/**
* return true if file is in filesList else return false
*/
static boolean isFileInList(File file, List<File> filesList) {
for(File f: filesList) {
if (f.equals(file)) {
return true;
}
}
return false;
}
public static void main(String[] args) {
List<File> files;// the filelist; make sure assign these two variable.
File file; // the file you want to test.
if (isFileInList(file, files)) {
//file is presented
} else {
//file is not presented
createFolder();
}
}
package test;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
public class DirectoryContents {
public static void main(String[] args) throws IOException {
File f = new File("."); // current directory
FilenameFilter textFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
String lowercaseName = name.toLowerCase();
if (lowercaseName.endsWith(".txt")) {
return true;
} else {
return false;
}
}
};
File[] files = f.listFiles(textFilter);
for (File file : files) {
if (file.isDirectory()) {
System.out.print("directory:");
} else {
System.out.print(" file:");
}
System.out.println(file.getCanonicalPath());
}
}
}
I've got a widget that allows the user to drag and drop an email message or a file into the widget to copy it to their file system. It's the FileExplorer project in OpenNTF, designed by people far more experienced than I am. I want to modify it to provide a new filename if the current filename already exists in the location they're dropping it on. With emails I'd hoped to be able to grab the sender and date, but I've been throwing errors when I try to access the file contents during a drag-and-drop of email.
So, my issue is actually simple. I've got the 'if' to determine if the filename is taken, but I'm overwhelmed trying to figure out how to test multiple options for the filename (like numbering then 'file1.eml', 'file2.eml', 'file3.eml'). I tried, below, inserting the word DUPLICATE, but I'm having no joy.
try {
if (source.isDirectory()) {
File dirTarget = new File(fDest.getAbsoluteFile() + File.separator + source.getName());
if (!dirTarget.exists()) {
dirTarget.mkdir();
}
copyDir(monitor, source, dirTarget);
}
if (source.isFile()) {
File dest = new File(fDest.getAbsolutePath() + File.separator + source.getName());
if (dest.getAbsolutePath().compareTo(source.getAbsolutePath()) != 0) {
copyFile(monitor, source, dest);
} else {
dest = new File(fDest.getAbsolutePath() + File.separator + "DUPLICATE" + File.separator + source.getName());
copyFile(monitor, source, dest);
}
}
} catch (IOException e) {
}
For reference, the copyFile method's parameters are
private void copyFile(IProgressMonitor monitor, File fSource, File fTarget) throws IOException
You need to construct your file name different.
File.seperator
results in / \ or : depending on your platform since it is the char separating the directory from the file.
Since you are dropping a file, you don't need check for the directory, up to you. You need a loop to test file names. To make it easy use (DUPLICATE 1) (DUPLICATE 2) etc. Something like this:
private final static String DUPLICATE = "DUPLICATE";
private void copyOut(File source, File fDest, Monitor monitor) {
try {
if (!source.exists() || !fDest.exists()) {
// one or two files missing, can't copy
// handle error here!
} else {
String destName = fDest.getAbsolutePath()+ File.separator + source.getName();
File dest = new File(destName);
if (source.isDirectory()) {
if (!dest.exists()) {
destPath.mkdirs(); // Fix missing
} else if (dest.isFile()) {
// Raise an error. Destination exists as file source is directory!!!
}
} else { // We checked for existence and dir, so it is a file
// Don't overwrite an existing file
dest = this.checkforDuplicate(dest);
}
copyFile(monitor, source, dest);
}
} catch (IOException e) {
// Error handling missing here!
}
}
private File checkforDuplicate(File dest) {
if (!dest.exists()) {
return dest;
}
int duplicateNum = 1;
while (true) {
ArrayList<String> pieces = Arrays.asList(dest.getAbsolutePath().split("."));
pieces.add(pieces.size()-1, DUPLICATE);
if (duplicateNum > 1) {
pieces.add(pieces.size()-1,Integer.toString(duplicateNum));
}
duplicateNum++;
StringBuilder newName = newStringBuilder();
for (String s : pieces) {
newName.append(s);
newName.append(".");
}
// Strip the last .
String outName = newName.substring(0, newName.length()-2);
File result = new File(outName);
if (!result.exists()) {
return result;
}
}
}
Check the code, written off memory, will contain typos. also doesn't deal with file names that don't contain a dot.
How can I delete one folder/directory by a non-recursive algorithm in Java? I want to use a non-recursive algorithm in order to avoid StackOverflowErrors when a folder has a very deep path.
Could someone please offer some advise in this area.
In crappy pseudo-code, as I don't have a Java compiler handy to test this:
queue = [ rootDir ]
stack = []
while ( !queue.isEmpty() ) {
currentDir = queue.take()
stack.push( currentDir )
files = currentDir.list()
for ( f : files ) {
if ( f.isDirectory() ) {
queue.add( f )
} else {
f.delete()
}
}
}
while ( !stack.isEmpty() ) {
f = stack.pop()
f.delete()
}
Basically this code should scan a directory, deleting files or queueing subdirectories for further scanning. It places scanned directories in a stack, so that the second while loop deletes them in the correct order (deepest first).
Here's a general way to delete a file/folder :
/**deletes a file/folder recursively, and returns true iff succeeded */
public static boolean deleteQuietly(File file) {
if (file == null || !file.exists())
return true;
if (!file.isDirectory())
return file.delete();
LinkedList<File> dirs = new LinkedList<>();
dirs.add(0, file);
boolean succeededDeletion = true;
while (!dirs.isEmpty()) {
file = dirs.remove(0);
File[] children = file.listFiles();
if (children == null || children.length == 0)
succeededDeletion &= file.delete();
else {
dirs.add(0, file);
for (File child : children)
if (child.isDirectory())
dirs.add(0, child);
else
succeededDeletion &= child.delete();
}
}
return succeededDeletion;
}
this is just a starting point for you to improve on.
The critical part is to find out what's the directories to delete.
This piece of psuedo code should help you to find out all directories under certain directory:
Set<File> allDirectories = new Set<File>();
allDirectories.add(yourStartingDirectory);
while (hasMoreToRead) {
hasMoreToRead = false;
for (File f : allDirectories) {
if (f.isDirectory() && !allDirectories.contains(f)) {
allDirectories.add(f);
hasMoreToRead = true;
}
}
}
This is just a starting point, but you should be able to finish the rest: Avoid revisiting directories in allDirectories that has been processed in previous iterations; Performing delete base on allDirectories; Make the delete more efficient by deleting in "correct" order; etc
// Deletes all files and subdirectories under dir.
// Returns true if all deletions were successful.
// If a deletion fails, the method stops attempting to delete and returns false.
public static boolean deleteDir(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (int i=0; i<children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
// The directory is now empty so delete it
return dir.delete();
}
To remove recursion, you replace the call stack with an explicit stack to hold the items you still need to process. In your case, you keep track of all the parent folders you need to delete after you are done with the current one. Here's an example using a LinkedList as a stack:
public static void rmdir(File dir) {
LinkedList<File> dirs = new LinkedList<File>();
dirs.push(dir);
while (dirs.peek() != null) {
dir = dirs.pop();
File[] contents = dir.listFiles();
if (contents.length == 0) {
dir.delete();
} else {
dirs.push(dir);
for(File content : contents) {
if (content.isDirectory()) {
dirs.push(content);
} else {
content.delete();
}
}
}
}
}
My interpretation of your question is that you want to delete a directory without recursing into the directories within it. In this case, you can implement the deletion using a pretty simple loop...
File directory = new File("c:\\directory_path")
if (!directory.exists()){
return;
}
File[] files = directory.listFiles();
for (int i=0;i<files.length;i++){
if (files[i].isFile()){
boolean deleted = files[i].delete();
if (!deleted){
System.out.println("Problem deleting file " + files[i].getAbsolutePath());
}
}
}
This will list all the Files of the directory in an array, and then loop through them. If the file is a normal file, it will be deleted. Non-normal files, such as directories, will be skipped.
Of course, there are other similar alternatives, such as adding a FileFilter to the listFiles() method so that the array is only populated by normal files, but its effectively pretty similar.
If you want to delete the directory tree, you will have to use some kind of recursion. You could approach it differently though, which might not cause you so many problems, such as building an ArrayList of directories, and then iterating through the ArrayList deleting them one at a time. This would help to reduce the recursion.
public static final void delete(File file) throws IOException
{
if (!file.exists())
throw new IllegalArgumentException("File does not exist: " + file);
if (file.isFile())
{
simpleDelete(file);
return;
}
Deque<File> dirsQueue = new ArrayDeque<File>();
dirsQueue.push(file);
for (File dir; (dir = dirsQueue.peekLast()) != null;)
{
File[] children = dir.listFiles();
if (children == null)
throw new IOException("Unable to read directory: " + dir);
if (children.length == 0)
{
simpleDelete(dir);
dirsQueue.removeLast();
continue;
}
for (File child : children)
{
if (child.isDirectory())
dirsQueue.addLast(child);
else
simpleDelete(child);
}
}
}
private static final void simpleDelete(File file) throws IOException
{
if (!file.delete())
throw new IOException("Unable to delete " + (file.isDirectory() ? "directory" : "file") + ": " + file);
}
How can I retrieve size of folder or file in Java?
java.io.File file = new java.io.File("myfile.txt");
file.length();
This returns the length of the file in bytes or 0 if the file does not exist. There is no built-in way to get the size of a folder, you are going to have to walk the directory tree recursively (using the listFiles() method of a file object that represents a directory) and accumulate the directory size for yourself:
public static long folderSize(File directory) {
long length = 0;
for (File file : directory.listFiles()) {
if (file.isFile())
length += file.length();
else
length += folderSize(file);
}
return length;
}
WARNING: This method is not sufficiently robust for production use. directory.listFiles() may return null and cause a NullPointerException. Also, it doesn't consider symlinks and possibly has other failure modes. Use this method.
Using java-7 nio api, calculating the folder size can be done a lot quicker.
Here is a ready to run example that is robust and won't throw an exception. It will log directories it can't enter or had trouble traversing. Symlinks are ignored, and concurrent modification of the directory won't cause more trouble than necessary.
/**
* Attempts to calculate the size of a file or directory.
*
* <p>
* Since the operation is non-atomic, the returned value may be inaccurate.
* However, this method is quick and does its best.
*/
public static long size(Path path) {
final AtomicLong size = new AtomicLong(0);
try {
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
size.addAndGet(attrs.size());
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
System.out.println("skipped: " + file + " (" + exc + ")");
// Skip folders that can't be traversed
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
if (exc != null)
System.out.println("had trouble traversing: " + dir + " (" + exc + ")");
// Ignore errors traversing a folder
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new AssertionError("walkFileTree will not throw IOException if the FileVisitor does not");
}
return size.get();
}
You need FileUtils#sizeOfDirectory(File) from commons-io.
Note that you will need to manually check whether the file is a directory as the method throws an exception if a non-directory is passed to it.
WARNING: This method (as of commons-io 2.4) has a bug and may throw IllegalArgumentException if the directory is concurrently modified.
In Java 8:
long size = Files.walk(path).mapToLong( p -> p.toFile().length() ).sum();
It would be nicer to use Files::size in the map step but it throws a checked exception.
UPDATE:
You should also be aware that this can throw an exception if some of the files/folders are not accessible. See this question and another solution using Guava.
public static long getFolderSize(File dir) {
long size = 0;
for (File file : dir.listFiles()) {
if (file.isFile()) {
System.out.println(file.getName() + " " + file.length());
size += file.length();
}
else
size += getFolderSize(file);
}
return size;
}
For Java 8 this is one right way to do it:
Files.walk(new File("D:/temp").toPath())
.map(f -> f.toFile())
.filter(f -> f.isFile())
.mapToLong(f -> f.length()).sum()
It is important to filter out all directories, because the length method isn't guaranteed to be 0 for directories.
At least this code delivers the same size information like Windows Explorer itself does.
Here's the best way to get a general File's size (works for directory and non-directory):
public static long getSize(File file) {
long size;
if (file.isDirectory()) {
size = 0;
for (File child : file.listFiles()) {
size += getSize(child);
}
} else {
size = file.length();
}
return size;
}
Edit: Note that this is probably going to be a time-consuming operation. Don't run it on the UI thread.
Also, here (taken from https://stackoverflow.com/a/5599842/1696171) is a nice way to get a user-readable String from the long returned:
public static String getReadableSize(long size) {
if(size <= 0) return "0";
final String[] units = new String[] { "B", "KB", "MB", "GB", "TB" };
int digitGroups = (int) (Math.log10(size)/Math.log10(1024));
return new DecimalFormat("#,##0.#").format(size/Math.pow(1024, digitGroups))
+ " " + units[digitGroups];
}
File.length() (Javadoc).
Note that this doesn't work for directories, or is not guaranteed to work.
For a directory, what do you want? If it's the total size of all files underneath it, you can recursively walk children using File.list() and File.isDirectory() and sum their sizes.
The File object has a length method:
f = new File("your/file/name");
f.length();
If you want to use Java 8 NIO API, the following program will print the size, in bytes, of the directory it is located in.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathSize {
public static void main(String[] args) {
Path path = Paths.get(".");
long size = calculateSize(path);
System.out.println(size);
}
/**
* Returns the size, in bytes, of the specified <tt>path</tt>. If the given
* path is a regular file, trivially its size is returned. Else the path is
* a directory and its contents are recursively explored, returning the
* total sum of all files within the directory.
* <p>
* If an I/O exception occurs, it is suppressed within this method and
* <tt>0</tt> is returned as the size of the specified <tt>path</tt>.
*
* #param path path whose size is to be returned
* #return size of the specified path
*/
public static long calculateSize(Path path) {
try {
if (Files.isRegularFile(path)) {
return Files.size(path);
}
return Files.list(path).mapToLong(PathSize::calculateSize).sum();
} catch (IOException e) {
return 0L;
}
}
}
The calculateSize method is universal for Path objects, so it also works for files.
Note that if a file or directory is inaccessible, in this case the returned size of the path object will be 0.
Works for Android and Java
Works for both folders and files
Checks for null pointer everywhere where needed
Ignores symbolic link aka shortcuts
Production ready!
Source code:
public long fileSize(File root) {
if(root == null){
return 0;
}
if(root.isFile()){
return root.length();
}
try {
if(isSymlink(root)){
return 0;
}
} catch (IOException e) {
e.printStackTrace();
return 0;
}
long length = 0;
File[] files = root.listFiles();
if(files == null){
return 0;
}
for (File file : files) {
length += fileSize(file);
}
return length;
}
private static boolean isSymlink(File file) throws IOException {
File canon;
if (file.getParent() == null) {
canon = file;
} else {
File canonDir = file.getParentFile().getCanonicalFile();
canon = new File(canonDir, file.getName());
}
return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
}
I've tested du -c <folderpath> and is 2x faster than nio.Files or recursion
private static long getFolderSize(File folder){
if (folder != null && folder.exists() && folder.canRead()){
try {
Process p = new ProcessBuilder("du","-c",folder.getAbsolutePath()).start();
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
String total = "";
for (String line; null != (line = r.readLine());)
total = line;
r.close();
p.waitFor();
if (total.length() > 0 && total.endsWith("total"))
return Long.parseLong(total.split("\\s+")[0]) * 1024;
} catch (Exception ex) {
ex.printStackTrace();
}
}
return -1;
}
for windows, using java.io this reccursive function is useful.
public static long folderSize(File directory) {
long length = 0;
if (directory.isFile())
length += directory.length();
else{
for (File file : directory.listFiles()) {
if (file.isFile())
length += file.length();
else
length += folderSize(file);
}
}
return length;
}
This is tested and working properly on my end.
private static long getFolderSize(Path folder) {
try {
return Files.walk(folder)
.filter(p -> p.toFile().isFile())
.mapToLong(p -> p.toFile().length())
.sum();
} catch (IOException e) {
e.printStackTrace();
return 0L;
}
public long folderSize (String directory)
{
File curDir = new File(directory);
long length = 0;
for(File f : curDir.listFiles())
{
if(f.isDirectory())
{
for ( File child : f.listFiles())
{
length = length + child.length();
}
System.out.println("Directory: " + f.getName() + " " + length + "kb");
}
else
{
length = f.length();
System.out.println("File: " + f.getName() + " " + length + "kb");
}
length = 0;
}
return length;
}
After lot of researching and looking into different solutions proposed here at StackOverflow. I finally decided to write my own solution. My purpose is to have no-throw mechanism because I don't want to crash if the API is unable to fetch the folder size. This method is not suitable for mult-threaded scenario.
First of all I want to check for valid directories while traversing down the file system tree.
private static boolean isValidDir(File dir){
if (dir != null && dir.exists() && dir.isDirectory()){
return true;
}else{
return false;
}
}
Second I do not want my recursive call to go into symlinks (softlinks) and include the size in total aggregate.
public static boolean isSymlink(File file) throws IOException {
File canon;
if (file.getParent() == null) {
canon = file;
} else {
canon = new File(file.getParentFile().getCanonicalFile(),
file.getName());
}
return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
}
Finally my recursion based implementation to fetch the size of the specified directory. Notice the null check for dir.listFiles(). According to javadoc there is a possibility that this method can return null.
public static long getDirSize(File dir){
if (!isValidDir(dir))
return 0L;
File[] files = dir.listFiles();
//Guard for null pointer exception on files
if (files == null){
return 0L;
}else{
long size = 0L;
for(File file : files){
if (file.isFile()){
size += file.length();
}else{
try{
if (!isSymlink(file)) size += getDirSize(file);
}catch (IOException ioe){
//digest exception
}
}
}
return size;
}
}
Some cream on the cake, the API to get the size of the list Files (might be all of files and folder under root).
public static long getDirSize(List<File> files){
long size = 0L;
for(File file : files){
if (file.isDirectory()){
size += getDirSize(file);
} else {
size += file.length();
}
}
return size;
}
in linux if you want to sort directories then du -hs * | sort -h
You can use Apache Commons IO to find the folder size easily.
If you are on maven, please add the following dependency in your pom.xml file.
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
If not a fan of Maven, download the following jar and add it to the class path.
https://repo1.maven.org/maven2/commons-io/commons-io/2.6/commons-io-2.6.jar
public long getFolderSize() {
File folder = new File("src/test/resources");
long size = FileUtils.sizeOfDirectory(folder);
return size; // in bytes
}
To get file size via Commons IO,
File file = new File("ADD YOUR PATH TO FILE");
long fileSize = FileUtils.sizeOf(file);
System.out.println(fileSize); // bytes
It is also achievable via Google Guava
For Maven, add the following:
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>
If not using Maven, add the following to class path
https://repo1.maven.org/maven2/com/google/guava/guava/28.1-jre/guava-28.1-jre.jar
public long getFolderSizeViaGuava() {
File folder = new File("src/test/resources");
Iterable<File> files = Files.fileTreeTraverser()
.breadthFirstTraversal(folder);
long size = StreamSupport.stream(files.spliterator(), false)
.filter(f -> f.isFile())
.mapToLong(File::length).sum();
return size;
}
To get file size,
File file = new File("PATH TO YOUR FILE");
long s = file.length();
System.out.println(s);
fun getSize(context: Context, uri: Uri?): Float? {
var fileSize: String? = null
val cursor: Cursor? = context.contentResolver
.query(uri!!, null, null, null, null, null)
try {
if (cursor != null && cursor.moveToFirst()) {
// get file size
val sizeIndex: Int = cursor.getColumnIndex(OpenableColumns.SIZE)
if (!cursor.isNull(sizeIndex)) {
fileSize = cursor.getString(sizeIndex)
}
}
} finally {
cursor?.close()
}
return fileSize!!.toFloat() / (1024 * 1024)
}