14.2 Files API Operations
Master the Files utility class for comprehensive file and directory operations.
Reading Files
Reading Text Files:
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
// Read entire file as string (small files)
Path configFile = Path.of("config.txt");
String content = Files.readString(configFile);
System.out.println(content);
// With specific charset
String utf16Content = Files.readString(configFile, StandardCharsets.UTF_16);
// Read all lines into List
List<String> lines = Files.readAllLines(configFile);
for (String line : lines) {
System.out.println(line);
}
// Read lines as stream (large files)
try (Stream<String> lineStream = Files.lines(configFile)) {
lineStream
.filter(line -> !line.startsWith("#"))
.map(String::trim)
.forEach(System.out::println);
}
// With charset
try (Stream<String> stream = Files.lines(configFile, StandardCharsets.UTF_8)) {
long count = stream.count();
System.out.println("Line count: " + count);
}
Reading Binary Files:
// Read entire file as byte array (small files)
Path imageFile = Path.of("photo.jpg");
byte[] imageData = Files.readAllBytes(imageFile);
System.out.println("Image size: " + imageData.length + " bytes");
// Read with InputStream (large files)
try (InputStream in = Files.newInputStream(imageFile)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
// Process buffer
processBytes(buffer, bytesRead);
}
}
// With StandardOpenOption
try (InputStream in = Files.newInputStream(
imageFile,
StandardOpenOption.READ)) {
// Read file
}
BufferedReader for Large Files:
Path largeFile = Path.of("large-dataset.csv");
try (BufferedReader reader = Files.newBufferedReader(largeFile)) {
String line;
int lineNum = 0;
while ((line = reader.readLine()) != null) {
lineNum++;
// Process line
if (lineNum % 10000 == 0) {
System.out.println("Processed " + lineNum + " lines");
}
}
}
// With charset
try (BufferedReader reader = Files.newBufferedReader(
largeFile,
StandardCharsets.UTF_8)) {
// Read content
}
Writing Files
Writing Text Files:
Path outputFile = Path.of("output.txt");
// Write string to file (overwrites by default)
String content = "Hello, World!\nThis is a test.";
Files.writeString(outputFile, content);
// With charset
Files.writeString(outputFile, content, StandardCharsets.UTF_8);
// With options
Files.writeString(
outputFile,
content,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND
);
// Write lines
List<String> lines = List.of(
"Line 1",
"Line 2",
"Line 3"
);
Files.write(outputFile, lines);
// With charset and options
Files.write(
outputFile,
lines,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING
);
Writing Binary Files:
Path binaryFile = Path.of("data.bin");
// Write byte array
byte[] data = {0x48, 0x65, 0x6C, 0x6C, 0x6F}; // "Hello"
Files.write(binaryFile, data);
// With options
Files.write(
binaryFile,
data,
StandardOpenOption.CREATE,
StandardOpenOption.WRITE
);
// Write with OutputStream
try (OutputStream out = Files.newOutputStream(binaryFile)) {
out.write(data);
out.flush();
}
// With StandardOpenOption
try (OutputStream out = Files.newOutputStream(
binaryFile,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.WRITE)) {
out.write(data);
}
BufferedWriter for Large Files:
Path logFile = Path.of("application.log");
try (BufferedWriter writer = Files.newBufferedWriter(
logFile,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND)) {
for (int i = 0; i < 100000; i++) {
writer.write("Log entry " + i);
writer.newLine();
if (i % 1000 == 0) {
writer.flush(); // Periodic flush
}
}
}
StandardOpenOption Combinations:
// Common combinations
Path file = Path.of("data.txt");
// Create new file (fails if exists)
Files.writeString(file, "content",
StandardOpenOption.CREATE_NEW,
StandardOpenOption.WRITE);
// Create or overwrite
Files.writeString(file, "content",
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.WRITE);
// Append to existing (create if doesn't exist)
Files.writeString(file, "more content",
StandardOpenOption.CREATE,
StandardOpenOption.APPEND);
// Write with sync (immediate flush to disk)
Files.writeString(file, "important data",
StandardOpenOption.WRITE,
StandardOpenOption.SYNC);
// Write atomically (on supported file systems)
Files.writeString(file, "atomic content",
StandardOpenOption.WRITE,
StandardOpenOption.DSYNC);
Copying Files
Basic Copy Operations:
Path source = Path.of("source.txt");
Path target = Path.of("target.txt");
// Simple copy (fails if target exists)
Files.copy(source, target);
// Copy with replace existing
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
// Copy with attributes
Files.copy(source, target,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES);
// Copy to OutputStream
try (OutputStream out = new FileOutputStream("output.bin")) {
Files.copy(source, out);
}
// Copy from InputStream
try (InputStream in = new FileInputStream("input.bin")) {
long bytesCopied = Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
System.out.println("Copied " + bytesCopied + " bytes");
}
Copying Directories:
/**
* Copy directory recursively
*/
public static void copyDirectory(Path source, Path target) throws IOException {
Files.walk(source).forEach(sourcePath -> {
try {
Path targetPath = target.resolve(source.relativize(sourcePath));
if (Files.isDirectory(sourcePath)) {
Files.createDirectories(targetPath);
} else {
Files.copy(sourcePath, targetPath,
StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
// Usage
Path sourceDir = Path.of("/source/directory");
Path targetDir = Path.of("/target/directory");
copyDirectory(sourceDir, targetDir);
Moving Files
Move Operations:
Path source = Path.of("/tmp/oldfile.txt");
Path target = Path.of("/var/data/newfile.txt");
// Simple move (fails if target exists)
Files.move(source, target);
// Move with replace
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
// Atomic move (on same filesystem)
Files.move(source, target,
StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.REPLACE_EXISTING);
// Rename file in same directory
Path file = Path.of("/data/oldname.txt");
Path renamed = file.resolveSibling("newname.txt");
Files.move(file, renamed);
Atomic Move for Safety:
/**
* Safe atomic file replacement
*/
public static void safeReplace(Path tempFile, Path targetFile) throws IOException {
try {
// Atomic move replaces target
Files.move(tempFile, targetFile,
StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.REPLACE_EXISTING);
} catch (AtomicMoveNotSupportedException e) {
// Fallback: copy then delete
Files.copy(tempFile, targetFile, StandardCopyOption.REPLACE_EXISTING);
Files.delete(tempFile);
}
}
// Usage pattern for safe writes
Path tempFile = Files.createTempFile("upload-", ".tmp");
try {
// Write to temp file
Files.writeString(tempFile, "important data");
// Atomically replace target
safeReplace(tempFile, Path.of("/var/data/important.txt"));
} catch (IOException e) {
// Clean up temp file on error
Files.deleteIfExists(tempFile);
throw e;
}
Deleting Files
Delete Operations:
Path file = Path.of("unwanted.txt");
// Delete (throws exception if doesn't exist)
Files.delete(file);
// Delete if exists (returns boolean)
boolean deleted = Files.deleteIfExists(file);
System.out.println("File deleted: " + deleted);
// Delete empty directory
Path emptyDir = Path.of("/tmp/emptydir");
Files.delete(emptyDir); // Fails if not empty
Deleting Directory Trees:
/**
* Delete directory recursively
*/
public static void deleteDirectory(Path directory) throws IOException {
if (!Files.exists(directory)) {
return;
}
Files.walk(directory)
.sorted(Comparator.reverseOrder()) // Delete files before directories
.forEach(path -> {
try {
Files.delete(path);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
// Usage
Path dir = Path.of("/tmp/old-data");
deleteDirectory(dir);
Safe Deletion with Verification:
public static boolean safeDelete(Path path) {
try {
if (!Files.exists(path)) {
return false;
}
// Verify we can delete (check permissions)
if (!Files.isWritable(path.getParent())) {
System.err.println("No write permission on parent directory");
return false;
}
// Delete
Files.delete(path);
// Verify deletion
if (Files.exists(path)) {
System.err.println("File still exists after deletion");
return false;
}
return true;
} catch (IOException e) {
System.err.println("Deletion failed: " + e.getMessage());
return false;
}
}
Creating Directories
Directory Creation:
// Create single directory
Path dir = Path.of("/tmp/newdir");
Files.createDirectory(dir);
// Create directory tree
Path deepDir = Path.of("/tmp/a/b/c/d");
Files.createDirectories(deepDir); // Creates all parent directories
// Check before creating
if (!Files.exists(dir)) {
Files.createDirectories(dir);
}
// With attributes (POSIX)
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-x---");
FileAttribute<Set<PosixFilePermission>> attrs =
PosixFilePermissions.asFileAttribute(perms);
Path secureDir = Path.of("/tmp/secure");
Files.createDirectory(secureDir, attrs);
File Attributes
Basic Attributes:
Path file = Path.of("document.txt");
// Read basic attributes
BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);
System.out.println("Size: " + attrs.size() + " bytes");
System.out.println("Created: " + attrs.creationTime());
System.out.println("Modified: " + attrs.lastModifiedTime());
System.out.println("Accessed: " + attrs.lastAccessTime());
System.out.println("Is directory: " + attrs.isDirectory());
System.out.println("Is regular file: " + attrs.isRegularFile());
System.out.println("Is symbolic link: " + attrs.isSymbolicLink());
// File key (unique identifier)
Object fileKey = attrs.fileKey();
System.out.println("File key: " + fileKey);
Quick Attribute Methods:
Path path = Path.of("file.txt");
// Common checks
boolean exists = Files.exists(path);
boolean notExists = Files.notExists(path);
boolean isDirectory = Files.isDirectory(path);
boolean isRegularFile = Files.isRegularFile(path);
boolean isSymbolicLink = Files.isSymbolicLink(path);
boolean isHidden = Files.isHidden(path);
boolean isReadable = Files.isReadable(path);
boolean isWritable = Files.isWritable(path);
boolean isExecutable = Files.isExecutable(path);
// File size
long size = Files.size(path);
// Last modified time
FileTime lastModified = Files.getLastModifiedTime(path);
// Set last modified time
FileTime newTime = FileTime.fromMillis(System.currentTimeMillis());
Files.setLastModifiedTime(path, newTime);
POSIX Attributes (Unix/Linux):
if (FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
Path file = Path.of("script.sh");
// Read POSIX attributes
PosixFileAttributes attrs = Files.readAttributes(file, PosixFileAttributes.class);
System.out.println("Owner: " + attrs.owner());
System.out.println("Group: " + attrs.group());
System.out.println("Permissions: " + PosixFilePermissions.toString(attrs.permissions()));
// Set permissions
Set<PosixFilePermission> perms = new HashSet<>();
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_WRITE);
perms.add(PosixFilePermission.OWNER_EXECUTE);
perms.add(PosixFilePermission.GROUP_READ);
perms.add(PosixFilePermission.GROUP_EXECUTE);
Files.setPosixFilePermissions(file, perms);
// Or use string notation
Set<PosixFilePermission> perms2 = PosixFilePermissions.fromString("rwxr-xr-x");
Files.setPosixFilePermissions(file, perms2);
// Change owner
UserPrincipal owner = FileSystems.getDefault()
.getUserPrincipalLookupService()
.lookupPrincipalByName("username");
Files.setOwner(file, owner);
// Change group
GroupPrincipal group = FileSystems.getDefault()
.getUserPrincipalLookupService()
.lookupPrincipalByGroupName("groupname");
Files.getFileAttributeView(file, PosixFileAttributeView.class)
.setGroup(group);
}
DOS Attributes (Windows):
if (FileSystems.getDefault().supportedFileAttributeViews().contains("dos")) {
Path file = Path.of("document.txt");
// Read DOS attributes
DosFileAttributes attrs = Files.readAttributes(file, DosFileAttributes.class);
System.out.println("Hidden: " + attrs.isHidden());
System.out.println("Archive: " + attrs.isArchive());
System.out.println("System: " + attrs.isSystem());
System.out.println("Read-only: " + attrs.isReadOnly());
// Set DOS attributes
DosFileAttributeView view = Files.getFileAttributeView(
file,
DosFileAttributeView.class
);
view.setHidden(true);
view.setArchive(true);
view.setReadOnly(false);
view.setSystem(false);
}
Generic Attributes:
Path file = Path.of("data.txt");
// Get single attribute
Object size = Files.getAttribute(file, "basic:size");
Object modified = Files.getAttribute(file, "basic:lastModifiedTime");
// Get multiple attributes
Map<String, Object> attrs = Files.readAttributes(file, "basic:*");
attrs.forEach((key, value) -> {
System.out.println(key + " = " + value);
});
// Set attribute
Files.setAttribute(file, "basic:lastModifiedTime",
FileTime.fromMillis(System.currentTimeMillis()));
// POSIX attributes
if (FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
Object permissions = Files.getAttribute(file, "posix:permissions");
System.out.println("Permissions: " + permissions);
Files.setAttribute(file, "posix:permissions",
PosixFilePermissions.fromString("rw-r--r--"));
}
Real-World Example: FileManager
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
/**
* Comprehensive file management utility
*/
public class FileManager {
/**
* Copy with progress callback
*/
public static void copyWithProgress(
Path source,
Path target,
ProgressCallback callback) throws IOException {
long totalSize = Files.size(source);
long copiedBytes = 0;
try (InputStream in = Files.newInputStream(source);
OutputStream out = Files.newOutputStream(target,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
copiedBytes += bytesRead;
if (callback != null) {
callback.onProgress(copiedBytes, totalSize);
}
}
}
}
/**
* Move with fallback
*/
public static void moveWithFallback(Path source, Path target) throws IOException {
try {
// Try atomic move first
Files.move(source, target,
StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.REPLACE_EXISTING);
} catch (AtomicMoveNotSupportedException e) {
// Fallback to copy and delete
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
Files.delete(source);
}
}
/**
* Safe write with backup
*/
public static void safeWrite(Path file, String content) throws IOException {
Path backup = null;
// Create backup if file exists
if (Files.exists(file)) {
backup = file.resolveSibling(file.getFileName() + ".bak");
Files.copy(file, backup, StandardCopyOption.REPLACE_EXISTING);
}
try {
// Write to temp file first
Path tempFile = Files.createTempFile(
file.getParent(),
"temp-",
".tmp"
);
try {
Files.writeString(tempFile, content);
// Atomic replace
Files.move(tempFile, file,
StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.REPLACE_EXISTING);
// Success - delete backup
if (backup != null) {
Files.deleteIfExists(backup);
}
} finally {
// Clean up temp file if still exists
Files.deleteIfExists(tempFile);
}
} catch (IOException e) {
// Restore from backup on failure
if (backup != null && Files.exists(backup)) {
Files.copy(backup, file, StandardCopyOption.REPLACE_EXISTING);
}
throw e;
}
}
/**
* Calculate directory size
*/
public static long calculateDirectorySize(Path directory) throws IOException {
AtomicLong size = new AtomicLong(0);
Files.walkFileTree(directory, 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) {
// Skip inaccessible files
System.err.println("Skipped: " + file + " (" + exc.getMessage() + ")");
return FileVisitResult.CONTINUE;
}
});
return size.get();
}
/**
* Find files by extension
*/
public static List<Path> findFilesByExtension(
Path directory,
String extension) throws IOException {
List<Path> results = new ArrayList<>();
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (file.toString().endsWith("." + extension)) {
results.add(file);
}
return FileVisitResult.CONTINUE;
}
});
return results;
}
/**
* Find old files
*/
public static List<Path> findOldFiles(
Path directory,
int daysOld) throws IOException {
Instant cutoff = Instant.now().minusSeconds(daysOld * 86400L);
List<Path> oldFiles = new ArrayList<>();
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (attrs.lastModifiedTime().toInstant().isBefore(cutoff)) {
oldFiles.add(file);
}
return FileVisitResult.CONTINUE;
}
});
return oldFiles;
}
/**
* Get file info summary
*/
public static String getFileInfo(Path path) throws IOException {
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
StringBuilder info = new StringBuilder();
info.append("Path: ").append(path.toAbsolutePath()).append("\n");
info.append("Size: ").append(formatSize(attrs.size())).append("\n");
info.append("Type: ");
if (attrs.isDirectory()) {
info.append("Directory\n");
} else if (attrs.isRegularFile()) {
info.append("Regular File\n");
} else if (attrs.isSymbolicLink()) {
info.append("Symbolic Link\n");
} else {
info.append("Other\n");
}
info.append("Created: ").append(attrs.creationTime()).append("\n");
info.append("Modified: ").append(attrs.lastModifiedTime()).append("\n");
info.append("Accessed: ").append(attrs.lastAccessTime()).append("\n");
// POSIX attributes if available
if (FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
PosixFileAttributes posixAttrs = Files.readAttributes(path, PosixFileAttributes.class);
info.append("Owner: ").append(posixAttrs.owner()).append("\n");
info.append("Group: ").append(posixAttrs.group()).append("\n");
info.append("Permissions: ")
.append(PosixFilePermissions.toString(posixAttrs.permissions()))
.append("\n");
}
return info.toString();
}
private static String formatSize(long bytes) {
if (bytes < 1024) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(1024));
String pre = "KMGTPE".charAt(exp - 1) + "";
return String.format("%.2f %sB", bytes / Math.pow(1024, exp), pre);
}
@FunctionalInterface
public interface ProgressCallback {
void onProgress(long current, long total);
}
// Example usage
public static void main(String[] args) throws IOException {
// Copy with progress
Path source = Path.of("large-file.zip");
Path target = Path.of("copy-of-large-file.zip");
copyWithProgress(source, target, (current, total) -> {
int percent = (int) ((current * 100) / total);
System.out.printf("\rCopying: %d%%", percent);
});
System.out.println("\nCopy complete!");
// Safe write
Path config = Path.of("config.properties");
safeWrite(config, "key=value\nfoo=bar\n");
// Directory size
Path dir = Path.of("/var/log");
long size = calculateDirectorySize(dir);
System.out.println("Directory size: " + formatSize(size));
// Find files
List<Path> javaFiles = findFilesByExtension(
Path.of("src"),
"java"
);
System.out.println("Found " + javaFiles.size() + " Java files");
// Find old files
List<Path> oldFiles = findOldFiles(Path.of("/tmp"), 7);
System.out.println("Found " + oldFiles.size() + " files older than 7 days");
// File info
System.out.println(getFileInfo(Path.of("README.md")));
}
}
Best Practices
1. Use Try-With-Resources for Streams:
// Always close streams
try (Stream<String> lines = Files.lines(path)) {
lines.forEach(System.out::println);
}
2. Choose Appropriate Methods:
// Small files
String content = Files.readString(path);
// Large files
try (Stream<String> lines = Files.lines(path)) {
// Process line by line
}
3. Use Atomic Operations:
// Atomic file replacement
Files.move(temp, target, StandardCopyOption.ATOMIC_MOVE);
4. Handle Platform Differences:
// Check file system capabilities
if (FileSystems.getDefault()
.supportedFileAttributeViews()
.contains("posix")) {
// Use POSIX attributes
}
5. Verify Operations:
// Verify after critical operations
Files.copy(source, target);
if (!Files.exists(target)) {
throw new IOException("Copy failed");
}
These Files API operations provide robust tools for all file management needs in modern Java applications.