|
4 | 4 | import java.io.File; |
5 | 5 | import java.io.IOException; |
6 | 6 | import java.lang.invoke.MethodHandles; |
| 7 | +import java.nio.file.FileSystems; |
| 8 | +import java.nio.file.FileVisitResult; |
7 | 9 | import java.nio.file.Files; |
| 10 | +import java.nio.file.LinkOption; |
| 11 | +import java.nio.file.Path; |
| 12 | +import java.nio.file.SimpleFileVisitor; |
| 13 | +import java.nio.file.attribute.BasicFileAttributes; |
| 14 | +import java.util.Set; |
8 | 15 |
|
| 16 | +import javax.annotation.Nonnull; |
9 | 17 | import javax.swing.JOptionPane; |
10 | 18 | import javax.swing.SwingUtilities; |
11 | 19 |
|
@@ -129,13 +137,109 @@ public static void deleteDirectory( final File dir ) throws IOException |
129 | 137 | * @param directory The directory whose size is to be calculated. Must not be null and must represent a valid directory. |
130 | 138 | * @return The total size of the directory in bytes. If the directory does not exist or is null, the method will return 0. |
131 | 139 | */ |
132 | | - public static long calculateDirectorySize( final File directory ) |
| 140 | + public static long calculateDirectorySize( File directory ) |
| 141 | + { |
| 142 | + if ( directory == null || !directory.exists() ) |
| 143 | + { |
| 144 | + return 0; |
| 145 | + } |
| 146 | + |
| 147 | + String os = System.getProperty( "os.name" ).toLowerCase(); |
| 148 | + boolean isUnix = os.contains( "nix" ) || os.contains( "nux" ) || os.contains( "mac" ); |
| 149 | + |
| 150 | + if ( isUnix ) |
| 151 | + { |
| 152 | + try |
| 153 | + { |
| 154 | + return calculateDiskUsageUnix( directory.toPath() ); |
| 155 | + } |
| 156 | + catch ( IOException e ) |
| 157 | + { |
| 158 | + // fallback to generic method if attribute not supported |
| 159 | + return calculateFileLengthRecursive( directory ); |
| 160 | + } |
| 161 | + } |
| 162 | + else |
| 163 | + { |
| 164 | + return calculateFileLengthRecursive( directory ); |
| 165 | + } |
| 166 | + } |
| 167 | + |
| 168 | + // --- Linux/macOS: actual disk usage, like du -sb --- |
| 169 | + private static long calculateDiskUsageUnix( Path directory ) throws IOException |
| 170 | + { |
| 171 | + final long[] total = { 0 }; |
| 172 | + boolean hasUnixView = supportsUnixAttributes(); |
| 173 | + |
| 174 | + Files.walkFileTree( directory, new SimpleFileVisitor< Path >() |
| 175 | + { |
| 176 | + @Nonnull |
| 177 | + @Override |
| 178 | + public FileVisitResult preVisitDirectory( @Nonnull Path dir, @Nonnull BasicFileAttributes attrs ) throws IOException |
| 179 | + { |
| 180 | + return Files.isSymbolicLink( dir ) |
| 181 | + ? FileVisitResult.SKIP_SUBTREE |
| 182 | + : FileVisitResult.CONTINUE; |
| 183 | + } |
| 184 | + |
| 185 | + @Nonnull |
| 186 | + @Override |
| 187 | + public FileVisitResult visitFile( @Nonnull Path file, @Nonnull BasicFileAttributes attrs ) throws IOException |
| 188 | + { |
| 189 | + if ( !Files.isSymbolicLink( file ) ) |
| 190 | + total[ 0 ] += getFileDiskUsage( file, hasUnixView ); |
| 191 | + return FileVisitResult.CONTINUE; |
| 192 | + } |
| 193 | + } ); |
| 194 | + |
| 195 | + return total[ 0 ]; |
| 196 | + } |
| 197 | + |
| 198 | + private static boolean supportsUnixAttributes() |
| 199 | + { |
| 200 | + Set< String > views = FileSystems.getDefault().supportedFileAttributeViews(); |
| 201 | + return views.contains( "unix" ); |
| 202 | + } |
| 203 | + |
| 204 | + /** |
| 205 | + * Returns the disk usage (in bytes) for a single file. |
| 206 | + * Uses actual allocated blocks if the unix view is supported. |
| 207 | + */ |
| 208 | + private static long getFileDiskUsage( Path file, boolean hasUnixView ) throws IOException |
| 209 | + { |
| 210 | + if ( !hasUnixView ) |
| 211 | + return Files.size( file ); |
| 212 | + |
| 213 | + try |
| 214 | + { |
| 215 | + Object blocksAttr = Files.getAttribute( file, "unix:blocks", LinkOption.NOFOLLOW_LINKS ); |
| 216 | + if ( blocksAttr instanceof Number ) |
| 217 | + return ( ( Number ) blocksAttr ).longValue() * 512L; // du uses 512-byte blocks |
| 218 | + } |
| 219 | + catch ( IllegalArgumentException | UnsupportedOperationException e ) |
| 220 | + { |
| 221 | + // ignore and fall back to logical size |
| 222 | + } |
| 223 | + return Files.size( file ); |
| 224 | + } |
| 225 | + |
| 226 | + // --- Windows / fallback: logical file sizes only --- |
| 227 | + private static long calculateFileLengthRecursive( File directory ) |
133 | 228 | { |
134 | 229 | long size = 0; |
135 | 230 | File[] files = directory.listFiles(); |
136 | 231 | if ( files != null ) |
| 232 | + { |
137 | 233 | for ( File file : files ) |
138 | | - size += file.isFile() ? file.length() : calculateDirectorySize( file ); |
| 234 | + { |
| 235 | + if ( Files.isSymbolicLink( file.toPath() ) ) |
| 236 | + continue; |
| 237 | + if ( file.isFile() ) |
| 238 | + size += file.length(); |
| 239 | + else if ( file.isDirectory() ) |
| 240 | + size += calculateFileLengthRecursive( file ); |
| 241 | + } |
| 242 | + } |
139 | 243 | return size; |
140 | 244 | } |
141 | 245 | } |
0 commit comments