如何使用Java 17检测两个Path是否为同一硬链接文件?
验证两个Path是否指向同一硬链接(Java 17)
跨平台通用方案:Files.isSameFile()
Java标准库的Files.isSameFile()方法可直接判断两个Path是否指向磁盘上的同一文件实体(包括硬链接),会自动适配不同操作系统的底层逻辑:
import java.nio.file.Files; import java.nio.file.Path; import java.io.IOException; public class HardLinkValidator { public static boolean areSameHardLink(Path path1, Path path2) { try { // 题目已说明共享同一FileStore,可省略此判断 if (!Files.getFileStore(path1).equals(Files.getFileStore(path2))) { return false; } return Files.isSameFile(path1, path2); } catch (IOException e) { // 处理文件不存在、权限不足等异常 return false; } } }
注意:若任一文件不存在,该方法会抛出
IOException,需做好异常捕获。
Unix类系统(Linux/macOS等)进阶验证
Unix系统中,同一硬链接的文件拥有相同的inode编号,可通过获取unix:ino属性判断:
public static boolean areSameHardLinkUnix(Path path1, Path path2) throws IOException { if (!Files.getFileStore(path1).equals(Files.getFileStore(path2))) { return false; } // 获取inode编号 long inode1 = (long) Files.getAttribute(path1, "unix:ino"); long inode2 = (long) Files.getAttribute(path2, "unix:ino"); return inode1 == inode2; }
若要判断文件是否存在其他硬链接,可获取硬链接计数属性unix:nlink:
long linkCount = (long) Files.getAttribute(path, "unix:nlink"); if (linkCount > 1) { // 文件存在其他硬链接 }
Windows/NTFS系统专项方案
方案1:调用fsutil命令解析输出
执行系统命令fsutil hardlink list <file-path>获取所有硬链接路径,再验证目标路径是否在列表中:
import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.nio.file.Path; public class WindowsHardLinkUtils { public static List<String> getHardLinkList(Path path) throws Exception { List<String> links = new ArrayList<>(); Process process = new ProcessBuilder("fsutil", "hardlink", "list", path.toString()).start(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { links.add(line.trim()); } } process.waitFor(); return links; } public static boolean areSameHardLinkWindows(Path path1, Path path2) throws Exception { return getHardLinkList(path1).contains(path2.toString()); } }
方案2:JNA调用Windows原生API
通过JNA调用GetFileInformationByHandle获取文件的唯一标识(卷序列号+文件索引),同一硬链接的标识完全一致:
- 引入JNA依赖(以Maven为例):
<dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>5.13.0</version> </dependency> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna-platform</artifactId> <version>5.13.0</version> </dependency>
- 编写验证代码:
import com.sun.jna.platform.win32.Kernel32; import com.sun.jna.platform.win32.WinNT; import com.sun.jna.platform.win32.WinBase; import java.nio.file.Path; import java.io.IOException; public class JNAHardLinkValidator { public static boolean areSameHardLink(Path path1, Path path2) throws IOException { // 打开文件句柄 WinNT.HANDLE handle1 = Kernel32.INSTANCE.CreateFile( path1.toString(), WinNT.GENERIC_READ, WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE | WinNT.FILE_SHARE_DELETE, null, WinNT.OPEN_EXISTING, WinBase.FILE_ATTRIBUTE_NORMAL, null ); WinNT.HANDLE handle2 = Kernel32.INSTANCE.CreateFile( path2.toString(), WinNT.GENERIC_READ, WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE | WinNT.FILE_SHARE_DELETE, null, WinNT.OPEN_EXISTING, WinBase.FILE_ATTRIBUTE_NORMAL, null ); try { if (WinNT.INVALID_HANDLE_VALUE.equals(handle1) || WinNT.INVALID_HANDLE_VALUE.equals(handle2)) { throw new IOException("无法打开文件句柄"); } WinBase.BY_HANDLE_FILE_INFORMATION info1 = new WinBase.BY_HANDLE_FILE_INFORMATION(); WinBase.BY_HANDLE_FILE_INFORMATION info2 = new WinBase.BY_HANDLE_FILE_INFORMATION(); if (!Kernel32.INSTANCE.GetFileInformationByHandle(handle1, info1) || !Kernel32.INSTANCE.GetFileInformationByHandle(handle2, info2)) { throw new IOException("无法获取文件信息"); } // 比较卷序列号和文件索引(高低位) return info1.dwVolumeSerialNumber.equals(info2.dwVolumeSerialNumber) && info1.nFileIndexHigh.equals(info2.nFileIndexHigh) && info1.nFileIndexLow.equals(info2.nFileIndexLow); } finally { // 关闭句柄 Kernel32.INSTANCE.CloseHandle(handle1); Kernel32.INSTANCE.CloseHandle(handle2); } } }
内容的提问来源于stack exchange,提问作者Thomas S.
相关产品推荐
相关产品推荐

