在 Java 中,我为内部用户创建了一个程序,用于在数据库中创建到网络驱动器上位置的映射。我通过从 javax.swing JFileChooser 获取 java.io.File 对象来完成此操作。
问题是一旦我有了 File 对象,当我需要完整的驱动器路径(例如 \shared-drive\.. \myDir\myFile.dat).
我更喜欢原生 Java 解决方案,但我不反对使用外部库,只要我可以从源代码构建即可。
我尝试使用 javax.swing FileSystemView 中的 getSystemDisplayName 方法,它似乎没有返回正确的结果。
我发现了三种从本地文件路径获取 Windows 上的 UNC 文件路径的不同方法。这些都使用 Java 8 进行了成功测试。我对其中任何一个都不是特别满意,但方法 #3 感觉它是我在 Windows 生态系统中的最佳选择。
import com.sun.jna.Memory;
import com.sun.jna.platform.win32.Mpr;
import com.sun.jna.platform.win32.Winnetwk;
import com.sun.jna.platform.win32.Winnetwk.REMOTE_NAME_INFO;
import com.sun.jna.ptr.IntByReference;
import java.io.File;
import java.io.InputStream;
import sun.awt.shell.ShellFolder;
import sun.awt.shell.ShellFolderColumnInfo;
public class Main {
public static void main(String[] args) throws Exception {
System.out.println(toUNCPath1("Z:\\a1.txt"));
System.out.println(toUNCPath2("Z:\\a1.txt"));
System.out.println(toUNCPath3("Z:\\a1.txt"));
}
public static String toUNCPath1(String filePath) {
try {
File rootDir = new File(filePath);
do {
rootDir = rootDir.getParentFile();
} while (rootDir.getParentFile() != null);
String drive = rootDir.getAbsolutePath().replace("\\", "");
ShellFolder folder = ShellFolder.getShellFolder(rootDir);
ShellFolderColumnInfo[] cols = folder.getFolderColumns();
for (int index = 0; index < cols.length; index++) {
if (cols[index].getTitle().equals("Attributes")) {
String unc = (String) folder.getFolderColumnValue(index);
return filePath.replace(drive, unc);
}
}
} catch (Exception err) {
err.printStackTrace();
}
return null;
}
public static String toUNCPath2(String filePath) {
try {
File rootDir = new File(filePath);
do {
rootDir = rootDir.getParentFile();
} while (rootDir.getParentFile() != null);
String drive = rootDir.getAbsolutePath().replace("\\", "");
StringBuilder attrBuilder = new StringBuilder();
StringBuilder pathBuilder = new StringBuilder();
ProcessBuilder processBuilder = new ProcessBuilder("net", "use", drive);
Process process = processBuilder.start();
InputStream inputStream = process.getInputStream();
while (true) {
int nextByte = inputStream.read();
if (nextByte == -1) {
break;
} else if (nextByte == 13 || nextByte == 10) {
attrBuilder.delete(0, attrBuilder.length());
} else {
attrBuilder.append((char) nextByte);
if (attrBuilder.toString().equals("Remote name")) {
while (true) {
nextByte = inputStream.read();
if (nextByte == 32) {
// pass
} else if (nextByte == 13 || nextByte == 10 || nextByte == -1) {
break;
} else {
pathBuilder.append((char) nextByte);
}
}
break;
}
}
}
inputStream.close();
return filePath.replace(drive, pathBuilder.toString().trim());
} catch (Exception err) {
err.printStackTrace();
}
return null;
}
private final static int MPR_BUFFER_SIZE = 1024; // MSDN recommends this as a reasonable size
public static String toUNCPath3(String filePath) {
try {
Memory memory = new Memory(MPR_BUFFER_SIZE);
IntByReference lpBufferSize = new IntByReference(MPR_BUFFER_SIZE);
Mpr.INSTANCE.WNetGetUniversalName(filePath, Winnetwk.REMOTE_NAME_INFO_LEVEL, memory, lpBufferSize);
REMOTE_NAME_INFO remoteNameInfo = new REMOTE_NAME_INFO(memory);
return remoteNameInfo.lpUniversalName;
} catch (Exception err) {
err.printStackTrace();
}
return null;
}
}
您还可以使用 JDK22 提供的 Java 代码和外部内存 API 直接调用 Windows API WNetGetUniversalNameW。这与不使用 JNA 的方法 #3 类似:
public static String toUNC(String filePath) {
try(Arena arena = Arena.ofConfined()) {
MemorySegment lpLocalPath = arena.allocateFrom(filePath, StandardCharsets.UTF_16LE);
MemorySegment lpBuffer = arena.allocate(JAVA_BYTE, MPR_BUFFER_SIZE);
MemorySegment lpBufferSize = arena.allocateFrom(Mpr_h.DWORD, MPR_BUFFER_SIZE);
int rc = Mpr_h.WNetGetUniversalNameW(lpLocalPath, Mpr_h.REMOTE_NAME_INFO_LEVEL(), lpBuffer, lpBufferSize);
//System.out.println("Size returned "+lpBufferSize.getAtIndex(Mpr_h.DWORD, 0));
if (rc == Mpr_h.WN_MORE_DATA())
throw new RuntimeException("Need a larger buffer than "+MPR_BUFFER_SIZE);
if (rc != Mpr_h.WN_NO_ERROR())
throw new RuntimeException("Unexpected result:"+rc);
// Interpret lpBuffer as REMOTE_NAME_INFO
MemorySegment remoteNameInfo = REMOTE_NAME_INFOW.reinterpret(lpBuffer, arena, null);
String name = REMOTE_NAME_INFOW.lpUniversalName(remoteNameInfo).getString(0, StandardCharsets.UTF_16LE);
System.out.println("toUNC("+filePath+") => "+name);
return name;
}
}
但是您需要使用 jextract 来生成代码绑定。使用头文件
Mpr.h
包含:
#include <shlobj_core.h>
#include <winnetwk.h>
定义文件中所需的符号集
Mpr.sym
--include-typedef DWORD
--include-constant WN_MORE_DATA
--include-constant WN_NO_ERROR
--include-constant WN_SUCCESS
--include-constant WN_WINDOWS_ERROR
--include-struct _REMOTE_NAME_INFOW
--include-typedef REMOTE_NAME_INFOW
--include-struct _UNIVERSAL_NAME_INFOW
--include-typedef UNIVERSAL_NAME_INFOW
--include-function WNetGetUniversalNameW
--include-constant REMOTE_NAME_INFO_LEVEL
生成 Java 中使用的定义的代码:
jextract -lMpr -t gen.code.mpr --output generatedsrc --dump-includes junit\gen\Mpr.sym junit\gen\Mpr.h