Java NTFS 压缩属性

标签 java ntfs file-attributes

我需要从 Java 读取和修改 NTFS 分区上文件的“压缩”属性。我在 java.nio.file.attribute 中想象了一些东西package 可以做到——见鬼,它是一个足够复杂的包,但我找不到这个属性。

DosFileAttributes 类只有经典的 hidden/system/readonly/archive 属性的 getter。

我试过了 Files.readAttributes它允许从特定的“属性 View ”动态检索所有属性。在“dos:*”下,只有 DosFileAttributes 类的公共(public)方法已经可用的相同属性。我尝试了“ntfs:*”和“windows:*”,但它们没有被接受为有效的 View 名称。

我也尝试了 UserDefinedFileAttributeView,但它在我尝试过的任何文件上都给了我一个空列表。

我想知道是否要支付给 attrib命令(接受它不适用于安装在 Linux 或其他操作系统下的 NTFS 分区的限制)但它似乎也不支持该属性。帮忙?

最佳答案

由于标准 Java API 似乎确实缺少此功能,因此我查看了自己使用 JNA 进行的操作.这是我第一次使用 JNA。它没有我想要的那么漂亮,而且 JNA 的代码似乎非常缺乏泛型,但它比使用 JNI 并尝试为所需的不同平台(最低 x86 和 x64)设置糟糕的交叉编译器要好一百倍即使您只针对一个操作系统)。最初是烦人的编译过程驱使我从 C++ 转向 Java,我希望永远不必回到它。

无论如何,这似乎可行。希望它对其他人也有用。它提供了四种公共(public)方法:

  • isAvailable() -- 调用其他方法是否有效(即,我们在 Windows 上并且 JNA native 库加载正常)
  • isCompressed(文件)
  • setCompressed(文件, boolean 值)
  • volumeSupportsFileCompression(File) -- 询问 Windows 文件所在的分区是否支持 [单个] 文件压缩。例如,在 NTFS 上为真,在 FAT(U 盘等)上为假。

Windows API 中的压缩是通过专用的 I/O 控制操作完成的,而不仅仅是“SetAttributes”调用。如果它更简单(与其他文件属性同构),为了完整起见,我也会将加密属性放在那里,但无论如何。

import java.io.File;
import java.io.IOException;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.ShortByReference;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinNT.HANDLE;

public class WindowsFileOps {
    private WindowsFileOps() {}


    private static interface Kernel32Extra extends StdCallLibrary {
        int COMPRESSION_FORMAT_NONE = 0x00000000;
        int COMPRESSION_FORMAT_DEFAULT = 0x00000001;
        int FSCTL_SET_COMPRESSION = 0x0009C040;

        Kernel32Extra INSTANCE = (Kernel32Extra)Native.loadLibrary("kernel32",
            Kernel32Extra.class, W32APIOptions.UNICODE_OPTIONS);

        boolean GetVolumeInformation(
            String lpRootPathName,
            Pointer lpVolumeNameBuffer,
            int nVolumeNameSize,
            IntByReference lpVolumeSerialNumber,
            IntByReference lpMaximumComponentLength,
            IntByReference lpFileSystemFlags,
            Pointer lpFileSystemNameBuffer,
            int nFileSystemNameSize
        );
    }


    private static Boolean isAvailable;
    public static boolean isAvailable() {
        if (isAvailable == null) {
            try {
                isAvailable = Kernel32.INSTANCE != null && Kernel32Extra.INSTANCE != null;
            } catch (Throwable t) {
                isAvailable = false;
            }
        }
        return isAvailable;
    }


    private static String pathString(File file) {
        // "\\?\" is a Windows API thing that enables paths longer than 260 chars
        return "\\\\?\\" + file.getAbsolutePath();
    }


    private static int getAttributes(File file) throws IOException {
        int attrib = Kernel32.INSTANCE.GetFileAttributes(pathString(file));
        if (attrib == Kernel32.INVALID_FILE_ATTRIBUTES) {
            throw new IOException("Unable to read file attributes of " + file);
        }
        return attrib;
    }


    public static boolean isCompressed(File file) throws IOException {
        return (getAttributes(file) & Kernel32.FILE_ATTRIBUTE_COMPRESSED) != 0;
    }


    public static void setCompressed(File file, boolean compressed) throws IOException {
        HANDLE hFile = Kernel32.INSTANCE.CreateFile(
            pathString(file),
            Kernel32.GENERIC_READ | Kernel32.GENERIC_WRITE,
            Kernel32.FILE_SHARE_READ,
            null,
            Kernel32.OPEN_EXISTING,
            0,
            null);
        try {
            if (!Kernel32.INSTANCE.DeviceIoControl(
                hFile,
                Kernel32Extra.FSCTL_SET_COMPRESSION,
                new ShortByReference((short)(
                    compressed
                        ? Kernel32Extra.COMPRESSION_FORMAT_DEFAULT
                        : Kernel32Extra.COMPRESSION_FORMAT_NONE
                )).getPointer(),
                2,
                null, 0,
                new IntByReference(),
                null
            )) throw new IOException("Unable to alter compression attribute of " + file);
        } finally {
            Kernel32.INSTANCE.CloseHandle(hFile);
        }
    }


    public static boolean volumeSupportsFileCompression(File file) throws IOException {
        IntByReference flags = new IntByReference();
        if (!Kernel32Extra.INSTANCE.GetVolumeInformation(
            pathString(file.getAbsoluteFile().toPath().getRoot().toFile()),
            null, 0,
            null,
            null,
            flags,
            null, 0
        )) throw new IOException("GetVolumeInformation failure");
        return (flags.getValue() & Kernel32.FILE_FILE_COMPRESSION) != 0;
    }
}

关于Java NTFS 压缩属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19401889/

相关文章:

java - 为什么要编译?类型删除

java - 用Java创建 "paint"应用程序

c# - 如何在 .NET/C# 中创建带尾随点 (.) 的文件夹?

c - 如何更改Linux文件属性

vba - 使用 VBA 获取扩展文件属性

java - 我的程序不会通过 HttpPost

java - JUnit with Kotlin - 此类没有构造函数

c++ - 我如何遍历目录并识别或省略 NTFS 连接(symlink-ish)

javascript - Node 生成过程并存储输出

delphi - FindFirst() 报告的奇怪属性值,Attr = 2080