Java - 如何从驱动器号获取Windows网络驱动器完整路径

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

在 Java 中,我为内部用户创建了一个程序,用于在数据库中创建到网络驱动器上位置的映射。我通过从 javax.swing JFileChooser 获取 java.io.File 对象来完成此操作。

问题是一旦我有了 File 对象,当我需要完整的驱动器路径(例如 \shared-drive\.. \myDir\myFile.dat).

我更喜欢原生 Java 解决方案,但我不反对使用外部库,只要我可以从源代码构建即可。

我尝试使用 javax.swing FileSystemView 中的 getSystemDisplayName 方法,它似乎没有返回正确的结果。

java windows swing
2个回答
1
投票

我发现了三种从本地文件路径获取 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;
    }
}

0
投票

您还可以使用 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
© www.soinside.com 2019 - 2024. All rights reserved.