跨平台检测符号链接/联接点的方法?

9
在Java中,Unix环境中的符号链接可以通过比较文件的规范路径和绝对路径来检测。然而,在Windows上这个技巧不起作用。如果我执行:
mkdir c:\foo
mklink /j c:\bar

从命令行开始,然后在Java中执行以下行:

File f = new File("C:/bar");
System.out.println(f.getAbsolutePath());
System.out.println(f.getCanonicalPath());

输出结果为

C:\bar
C:\bar

有没有在Windows中检测连接点的Java 7之前的方法?

你可以使用JNI方法获取文件属性并检查它是否为重分析点。 - mikek3332002
2
顺便提一下,Java 7也不会将联接点识别为符号链接。 - Hakanai
4个回答

8

在Java 6或更早版本中似乎没有跨平台机制来实现这一点,但使用JNA可以轻松完成这个简单的任务。

interface Kernel32 extends Library {
  public int GetFileAttributesW(WString fileName);
}

static Kernel32 lib = null;
public static int getWin32FileAttributes(File f) throws IOException { 
  if (lib == null) {
    synchronized (Kernel32.class) {
      lib = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
    }
  }
  return lib.GetFileAttributesW(new WString(f.getCanonicalPath()));
}

public static boolean isJunctionOrSymlink(File f) throws IOException {
  if (!f.exists()) { return false; }
  int attributes = getWin32FileAttributes(f);
  if (-1 == attributes) { return false; }
  return ((0x400 & attributes) != 0);
}

编辑:根据可能由getWin32FileAttributes()返回的错误返回,进行了更新


只是提醒一下,如果 GetFileAttributesW() 失败,它会返回 0xFFFFFFFF(作为 int-1)。在 isJunctionOrSymlink() 检查 0x400 标志之前,需要检查该条件,否则它将返回错误的结果。由于如果文件不存在,GetFileAttributesW() 将失败,因此对 f.exists() 的检查是多余的。 - Remy Lebeau

1

答案是“不行”。连接点和符号链接不是同一种东西。JRE不会检查它们,因此您引用的函数不会区分它们。

话虽如此,您可以通过以下方式实现某些功能:

如果联接目录具有内容,则在其下方获取规范路径名的结果可能会“令人惊讶”并揭示情况,因为它很可能是联接目标下的路径名。当然,这仅适用于联接指向的目录非空的情况。


1
Windows中的连接点是一个文件系统实体,它将您重定向到另一个位置(可能在另一个卷上),并对于打开和读取文件的所有文件系统API都是透明的。这难道不与(POSIX)符号链接完全相同吗?除了在Windows上,如果它是用于目录,则称为联接,如果是用于文件,则称为符号链接? - Jherico
我的问题的重点是,与Linux上的JVM不同,Windows上的JVM将不会为联接点(或目录符号链接或其中的任何内容)返回不同的规范和绝对值。 - Jherico
在Linux文件系统中,inode中有一个指向真正父目录的反向指针,而不是任何符号链接。在NTFS中也是如此。类似地,junction point就像是一个挂载点 - bmargulies

0

使用nio,即使没有反射或使用JNA也可以检测到连接点/重新解析点或符号链接:

public void isReparsePointOrSymlink(Path path) {
    final var attribute = Files.getAttribute(path, "dos:attributes", LinkOption.NOFOLLOW_LINKS);
    if (attribute instanceof Integer value) {
        final boolean isJunctionOrSymlink = (value & 0x400) != 0;
        return isJunctionOrSymlink;
    }
    return Files.isSymbolicLink(path);
}

请注意,Files.isSymbolicLink(path)仅对符号链接返回true,而不是重解析点。如果您需要区分两者,并且isReparsePointOrSymlink返回true,还要检查Files.isSymbolicLink

-1
你可以尝试在Windows上使用这个不太正规的技巧。
    if (f.isDirectory() || f.isFile()) {
        System.out.println("File or Directory");
    } else {
        System.out.println("Link");
    }

1
这并没有什么用。isDirectory()会对普通目录、目录符号链接(使用mklink /d创建)或联接(使用mklink /j创建)返回true。任何其他类型的链接或快捷方式都将对isFile()返回true。你可能会感到困惑,因为如果您创建一个指向目录或文件的快捷方式,它会显示为“Foo”,但在磁盘上表示为“Foo.lnk”,因此isDirectory()和isFile()将对“Foo”返回false(但exists()也是如此)。无论如何,快捷方式只是Windows资源管理器的产物,与我的问题无关。 - Jherico

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接