之前想写一个小的练手项目的时候突然有对这个的需求,但是网上全是到处搬运的错误的、不完整的代码,要不就是只给列个根目录出来…最后找了半天发现JDK本身就支持这个,于是就写出来分享一下。
完整代码
仅展示测试代码,实际使用时可能还需要改动。 测试环境:
- 操作系统:Windows 10 22H2
- Java版本:17
- JDK:GraalVM 17.0.7+8.1(Hotspot模式)
package main;
import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.DecimalFormat;
import java.util.HashMap;
/**
* 使用Java获取磁盘基本信息.
* @author Denvo
*/
public class Main {
public static void main(String[] args) {
System.out.print("\n");
Iterable roots = FileSystems.getDefault().getRootDirectories(); //获取所有的磁盘的根目录.
for (Path root:roots) {
System.out.println(root.toString() + "信息:" + getDiskInfo(root)); //getDiskInfo()方法在下面实现.
}
}
/**
* 根据卷根目录获取卷的信息.
* @param root 根目录,如"C:\","D:\"
* @return 包含卷的信息的HashMap.
*/
private static HashMap getDiskInfo(Path root) {
HashMap result = new HashMap(8);
try {
FileStore fs = Files.getFileStore(root);
result.put("卷标",fs.name());
result.put("文件存储类型",fs.type());
result.put("只读",String.valueOf(fs.isReadOnly()));
result.put("总容量",autoUnitConversion(fs.getTotalSpace())); //autoUnitConversion()方法在下面实现.
result.put("可用容量",autoUnitConversion(fs.getUsableSpace()));
result.put("每个扇区大小",fs.getBlockSize() + " Bytes");
//以下仅Windows可用.
result.put("可移动",fs.getAttribute("volume:isRemovable").toString());
result.put("是否为CD-ROM",fs.getAttribute("volume:isCdrom").toString());
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 单位转换,从Byte转换成Byte,KB,MB,GB,TB,PB,EB等.
* @param bytesSize 原始大小(Byte)
* @return 换算之后的大小(自动添加单位)
*/
private static String autoUnitConversion(long bytesSize) {
if (bytesSize < 0) {
return "0";
} else {
double before = bytesSize;
double after;
for (int i = 0; i < 7; i++) {
after = before / 1024;
if (after < 1) {
String unit = switch (i) {
case 0 -> " Bytes";
case 1 -> " KB";
case 2 -> " MB";
case 3 -> " GB";
case 4 -> " TB";
case 5 -> " PB";
case 6 -> " EB";
default -> null;
};
return new DecimalFormat("#.##").format(before) + unit; //这里设置保留2位小数.
} else {
before = after;
}
}
return "8 EB或更大"; //long类型的最大值换算过来最大只有8EB.(正常情况下都应该从上面for循环中return.)
}
}
}
代码运行结果
我的电脑目前的磁盘有如下,其中G盘到J盘都是U盘。
以上代码的运行结果如下:
C:\信息:{文件存储类型=NTFS, 总容量=223.15 GB, 可用容量=93.87 GB, 可移动=false, 是否为CD-ROM=false, 每个扇区大小=512 Bytes, 卷标=DenvosWindows, 只读=false}
D:\信息:{文件存储类型=NTFS, 总容量=447.13 GB, 可用容量=340.74 GB, 可移动=false, 是否为CD-ROM=false, 每个扇区大小=512 Bytes, 卷标=SSD_Data, 只读=false}
E:\信息:{文件存储类型=NTFS, 总容量=931.51 GB, 可用容量=455.83 GB, 可移动=false, 是否为CD-ROM=false, 每个扇区大小=512 Bytes, 卷标=DenvosData, 只读=false}
F:\信息:{文件存储类型=NTFS, 总容量=1.82 TB, 可用容量=809.44 GB, 可移动=false, 是否为CD-ROM=false, 每个扇区大小=512 Bytes, 卷标=HDD_Data, 只读=false}
G:\信息:{文件存储类型=FAT, 总容量=125.7 MB, 可用容量=115.87 MB, 可移动=true, 是否为CD-ROM=false, 每个扇区大小=512 Bytes, 卷标=, 只读=false}
H:\信息:{文件存储类型=NTFS, 总容量=29.3 GB, 可用容量=5.59 GB, 可移动=true, 是否为CD-ROM=false, 每个扇区大小=512 Bytes, 卷标=, 只读=false}
I:\信息:{文件存储类型=NTFS, 总容量=57.73 GB, 可用容量=31.68 GB, 可移动=true, 是否为CD-ROM=false, 每个扇区大小=512 Bytes, 卷标=KINGSTON, 只读=false}
J:\信息:{文件存储类型=exFAT, 总容量=14.52 GB, 可用容量=7.5 GB, 可移动=true, 是否为CD-ROM=false, 每个扇区大小=512 Bytes, 卷标=Ventoy, 只读=false}
代码分析
实现功能的代码主要在getDiskInfo()
方法中。首先通过FileStore fs = Files.getFileStore(Path root);
来获取一个FileStore
对象,然后通过这个对象来获取磁盘的相应信息的。
上面的代码中创建的fs
对象的实现在java.base
包中的sun.nio.fs.WindowsFileStore
类,而这个类又继承了来自同一个包的抽象类java.nio.file.FileStore
类。上面的代码中获取磁盘信息的方法实际上都是使用了被WindowsFileStore
类重写的、来自FileSotre
类中的方法。
在WindowsFileSotre
类的实现中,主要使用了java.base
包中的sun.nio.fs.WindowsNativeDispatcher
类中的方法,而这个类有很多方法都是native方法,即使用了JNI调用了位于%JAVA_HOME%/bin
目录中的net.dll
和nio.dll
两个dll中C/C++的方法(JDK文档说nio.dll
需要net.dll
作为依赖)
例如获取这个磁盘是否可移动的方法fs.getAttribute("volume:isRemovable")
实际上调用了nio.dll
中的方法Java_sun_nio_fs_WindowsNativeDispatcher_GetDriveType0
,相对虚拟地址在00006078
。