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

cwxwcias  于 2022-12-24  发布在  Windows
关注(0)|答案(6)|浏览(145)

我一直在四处寻找,试图找到一种方法来确定一个文件是否是一个连接点,但没有找到任何令人满意的答案。
我尝试的第一件事是:

Files.isSymbolicLink(aPath)

它只检测符号链接,而不检测Windows中称为连接的文件。
还尝试了这里提出的解决方案(使用JNA库):Stackoverflow question (3249117),但它从未在我知道的任何连接文件上返回true。
我发现确定哪些文件是连接的唯一方法是在Windows命令提示符下运行以下命令:

DIR /S /A:L

在我的计算机上,它返回66个文件夹,而Files.isSymbolicLink(aPath)只返回2个。所以我想我可以找到一种方法来利用它,但我不认为它在遍历文件树时会非常有效。
有没有什么方法可以使用标准java库或者JNA来实现这一点?

7kqas0il

7kqas0il1#

如果你有正确的java,比如Oraclejdk8,那么没有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)
如果它不是,但它是重解析点,那么它是连接点。

qacovj5a

qacovj5a2#

如果可以用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 Hard Links, Directory Junctions, and Windows Shortcuts
1.使用FindFirstFile()获取WIN32_FIND_DATA结构。如果路径具有FILE_ATTRIBUTE_REPARSE_POINT属性,则dwReserved0字段将包含ReparseTag

lkaoscv7

lkaoscv73#

对于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;
}
wfsdck30

wfsdck304#

在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();
nxowjjhe

nxowjjhe5#

黑盒解决方案:

aPath.toRealPath()解析交点和符号链接,因此结果将偏离aPath
此外,由于未记录的原因,BasicFileAttributes.isSymbolicLink()为交叉点提供false
例如,Path.toRealPath(LinkOption.NOFOLLOW_LINKS)会将交汇点视为链接,但不会解析它!!
因此,通过toRealPath()BasicFileAttributes.isSymbolicLink()的非同一性,你可以识别出一个结点。

nvbavucw

nvbavucw6#

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

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() {
    }
}

相关问题