Java NIO递归重命名含文件文件夹遇AccessDeniedException求助
Hey there, let's break down why you're hitting that AccessDeniedException and how to fix it.
The Root Cause
On Windows (which your path structure indicates you're using), when you use Files.walkFileTree, the file system traversal context keeps a handle open to each directory even during the postVisitDirectory phase. Trying to rename the directory while this handle is still held will trigger an access denied error—Windows locks directories that are actively being accessed by a process. Your current approach renames directories mid-traversal, which conflicts with this locking behavior.
The Solution: Collect First, Rename Later
Instead of renaming files/directories while traversing the tree, we'll:
- Traverse the entire directory structure and collect all paths that need renaming.
- Sort the directory paths so we rename deepest subdirectories first, then work our way up to parent directories. This ensures we don't try to rename a parent directory before its children are already renamed.
- Rename all eligible files first, then process the sorted directories.
Modified Code
Here's the adjusted version of your code that implements this approach:
package com.din.pach; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Comparator; import java.util.List; public class FileNio { // Lists to hold paths that need renaming private static final List<Path> filesToRename = new ArrayList<>(); private static final List<Path> dirsToRename = new ArrayList<>(); private static final String PREFIX = "POPCORN-"; public static void main(String[] args) throws IOException { Path sourcePath = Paths.get("D:\\root\\"); // First pass: collect all files/dirs that need renaming Files.walkFileTree(sourcePath, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { String fileName = file.getFileName().toString(); if (fileName.startsWith(PREFIX)) { filesToRename.add(file); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) { String dirName = dir.getFileName().toString(); if (dirName.startsWith(PREFIX)) { dirsToRename.add(dir); } return FileVisitResult.CONTINUE; } }); // Rename files first for (Path file : filesToRename) { renamePath(file); } // Sort directories by depth (deepest first) dirsToRename.sort(Comparator.comparingInt(path -> path.getNameCount()).reversed()); // Rename directories from deepest to shallowest for (Path dir : dirsToRename) { renamePath(dir); } } private static void renamePath(Path path) throws IOException { String originalName = path.getFileName().toString(); String newName = originalName.replaceFirst(PREFIX, ""); Path newPath = path.resolveSibling(newName); // Use REPLACE_EXISTING to handle any existing files/dirs with the target name Files.move(path, newPath, StandardCopyOption.REPLACE_EXISTING); System.out.printf("Renamed: %s -> %s%n", path, newPath); } }
Key Changes Explained
- Separate Collection Phase: We first gather all paths that need renaming without modifying anything during traversal. This ensures the file system traverser can complete its work without interference, releasing all directory handles.
- Directory Sorting: By sorting directories by their depth (reversed), we guarantee that we rename subdirectories before their parent directories. This avoids any issues where a parent directory rename would break references to unprocessed children.
- Simplified Rename Logic: The
renamePathmethod works for both files and directories, reducing code duplication.
Additional Notes
- Make sure you're running the program with administrative privileges if the
D:\rootdirectory is in a protected location (though your error suggests permissions aren't the core issue here). - The
SimpleFileVisitoris used instead of implementing allFileVisitormethods, which cleans up the code.
内容的提问来源于stack exchange,提问作者Dinesh Ravi

