确定文件是否是连接(在 Windows 中)?

问题描述 投票:0回答:6

我一直在寻找一种方法来确定文件是否是连接,但没有找到任何令人满意的答案。

我尝试的第一件事是:

Files.isSymbolicLink(aPath)

它仅检测符号链接,而不检测 Windows 中称为连接的文件。

还尝试了此处提出的解决方案(使用 JNA 库): Stackoverflow 问题 (3249117) ,但在我知道是连接的任何文件上它从未返回 true。

我发现确定哪些文件是连接的唯一方法是在 Windows 命令提示符中运行以下命令:

DIR /S /A:L

在我的计算机上它返回 66 个文件夹,而 Files.isSymbolicLink(aPath) 仅返回 2 个。 所以我想我可以找到一种方法来利用它,但我认为在遍历文件树时它不会非常有效。

有没有办法使用标准 java 库或 JNA 来做到这一点?

java windows winapi jna
6个回答
9
投票

如果您可以在 JNA 中编写本机代码,则可以直接调用 Win32 API

GetFileAttributes()
函数并检查
FILE_ATTRIBUTE_REPARSE_POINT
标志(连接点被实现为重解析点)。

更新:为了区分不同类型的重解析点,您必须检索实际重解析点的

ReparseTag
。 对于连接点,它将设置为
IO_REPARSE_TAG_MOUNT_POINT
(0xA0000003)。

有两种方法可以检索

ReparseTag

  1. 使用
    DeviceIoControl()
    FSCTL_GET_REPARSE_POINT
    控制代码来获取
    REPARSE_DATA_BUFFER
    结构体,作为
    ReparseTag
    字段。 您可以在以下文章中查看使用此技术的
    IsDirectoryJunction()
    实现示例:

NTFS 硬链接、目录连接和 Windows 快捷方式

  1. 使用
    FindFirstFile()
    获取
    WIN32_FIND_DATA
    结构体。如果路径具有
    FILE_ATTRIBUTE_REPARSE_POINT
    属性,则
    dwReserved0
    字段将包含
    ReparseTag

9
投票

如果你有正确的java,例如Oracle jdk 8,可以有一种方法可以在没有JNA的情况下做到这一点。它很狡猾,它可能会停止工作,但是....

可以获取BasicFileAttributes接口相关链接:

BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);

这个接口实现可能是一个类

sun.nio.fs.WindowsFileAttributes
。这个类有一个方法
isReparsePoint
,它对于连接点和符号链接都返回 true。所以你可以尝试使用反射并调用该方法:

    boolean isReparsePoint = false;
    if (DosFileAttributes.class.isInstance(attr))
        try {
            Method m = attr.getClass().getDeclaredMethod("isReparsePoint");
            m.setAccessible(true);
            isReparsePoint = (boolean) m.invoke(attr);
        } catch (Exception e) {
            // just gave it a try
        }

现在只能判断是否真的是符号链接:

Files.isSymbolicLink(path)

如果不是,但它是重解析点,那么那就是连接点。


8
投票

在 Windows 上,连接点的属性有

isSymbolicLink()
==
false
,它们有
isOther()
==
true
。所以你可以这样做:

boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows")
BasicFileAttributes attrs = Files.readAttributes(aPath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
boolean isJunction = isWindows && attrs.isDirectory() && attrs.isOther();

7
投票

在 J2SE 1.7 中使用 Java NIO

/**
* returns true if the Path is a Windows Junction
*/
private static boolean isJunction(Path p) {
    boolean isJunction = false;
    try {
        isJunction = (p.compareTo(p.toRealPath()) != 0);
    } catch (IOException e) {
        e.printStackTrace(); // TODO: handleMeProperly
    }
    return isJunction;
}

0
投票

黑盒解决方案:

aPath.toRealPath()
解析连接点和符号链接,因此结果会偏离
aPath

此外,由于未记录的原因,

BasicFileAttributes.isSymbolicLink()
还为路口提供
false

例如

Path.toRealPath(LinkOption.NOFOLLOW_LINKS)
很好地将连接点视为链接,但无法解析它!!

因此,通过

toRealPath()
BasicFileAttributes.isSymbolicLink()
的非同一性,您可以识别出一个交汇点。


0
投票

您可以使用 PowerShell 命令发现链接类型

(获取项目-路径文件名-Force).LinkType

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

class WindowsFileLinkUtils {

    public enum WindowsLinkType {
        JUNCTION("Junction"),
        HARD_LINK("HardLink"),
        SYMBOLIC_LINK("SymbolicLink");

        private final String key;

        WindowsLinkType(String key) {
            this.key = key;
        }

        public String getKey() {
            return key;
        }
    }

    private static final String CREATE_JUNCTION_COMMAND = "(Get-Item -Path %s -Force).LinkType";

    public static Optional<WindowsLinkType> getLinkType(Path path) throws IOException, InterruptedException {
        ProcessBuilder processBuilder = createIsJunctionProcessBuilder(path);
        Process process = processBuilder.start();
        process.waitFor();

        try (BufferedReader inStreamReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            String output = inStreamReader.readLine();
            return Arrays.stream(WindowsLinkType.values()).filter(windowsLinkType -> windowsLinkType.getKey().equals(output)).findFirst();
        }
    }

    private static ProcessBuilder createIsJunctionProcessBuilder(Path target) {
        ProcessBuilder processBuilder = new ProcessBuilder();
        List<String> arguments = processBuilder.command();
        arguments.add("powershell.exe");

        arguments.add(String.format(CREATE_JUNCTION_COMMAND, target.toString()));
        return processBuilder;
    }

    private WindowsFileLinkUtils() {
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.