跳至主要內容

Java SMBJ库的基本使用

Mr.Twan大约 4 分钟编程笔记javasmbsmbj

业务场景

使用它是因为客户要求网盘中能操作共享文件夹,但因为服务器是一台windows server2012+ 貌似是不支持smb1协议,故而jcifs并不适用,目前Java实现的开源smb2/smb3库我找到的只有SMBJ

SMBJ

它GitHub是这样说的:SMB2/SMB3 client library for Java 项目github地址:SMBJopen in new window API文档地址:https://www.javadoc.io/doc/com.hierynomus/smbjopen in new window

简单的使用Demo

仅包含基本使用:读和写

1、创建会话


/**
 * Windows10 已弃用smb1 需要使用SMBJ
 *
 * @author Twan - Twan
 * @version v1.0
 * @date 2020/11/5
 * @since v1.0
 */
@Configuration
public class RemoteFileForSMBV2 {
    /**
     * ip
     **/
    private final String hostName = "192.168.43.35";
    /**
     * 目标用户名(如需密码则填写上即可此处可以@Value写到配置文件中)
     **/
    private final String username = "";
    /**
     * 密码
     **/
    private final String password = "";

    @Bean(name = "Smb2Session")
    public Session getSmb2Session() {
//        String hostName = "192.168.1.106";

        Session s = null;
        try {
            SMBClient client = new SMBClient(SmbConfig.createDefaultConfig());
            Connection c = client.connect(hostName);
            System.out.println("是否链接:" + c.isConnected());
            s = c.authenticate(new AuthenticationContext(username, password.toCharArray(), ""));
            return s;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return s;
    }
}

2、实现简单的读写

接口:

/**
 * 磁盘共享接口
 *
 * @author Twan - Twan
 * @version v1.0
 * @date 2020/11/5
 * @since v1.0
 */
public interface DiskShareHandleInterface {
    List<String> listByShareName(String shareName);

    boolean writeFile(String shareName, String filePath) throws IOException;
}

实现类:

/**
 * 读写操作
 *
 * @author Twan - Twan
 * @version v1.0
 * @date 2020/11/5
 * @since v1.0
 */
@Component
public class HandleDiskShare implements DiskShareHandleInterface {

    @Resource(name = "Smb2Session")
    private Session session;

    @Override
    public List<String> listByShareName(String shareName) {
        ArrayList<String> strings = new ArrayList<>();
        // 这块官方有示例
        DiskShare share = (DiskShare) session.connectShare(shareName);
        List<FileIdBothDirectoryInformation> list = share.list("");
        for (FileIdBothDirectoryInformation information : list) {
            strings.add(information.getFileName());
        }
        return strings;
    }


    @Override
    public boolean writeFile(String shareName, String filePath) throws IOException {
//        filePath = shareName + filePath;
        DiskShare share = (DiskShare) session.connectShare(shareName);
        File f = null;
        int idx = filePath.lastIndexOf("/");
        String folder = "";
        String onlyFileName = filePath.substring(idx + 1, filePath.length());
        if (idx > -1) {
            folder = filePath.substring(0, idx);
        }
		// 文件不存在则创建,存在则打开。
        try {
            System.out.println(folder + java.io.File.separator + onlyFileName);
            f = share.openFile(folder + onlyFileName, new HashSet(
                    Arrays.asList(new AccessMask[]{AccessMask.GENERIC_ALL})),
                    (Set) null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_CREATE,
                    (Set) null);
        } catch (SMBApiException e) {
        	// 此处取了个巧,捕获了其文件已存在时抛出的异常
            if (e.getMessage().contains("STATUS_OBJECT_NAME_COLLISION")) {
                System.out.println("文件已经存在执行打开操作");
                System.out.println(folder + java.io.File.separator + onlyFileName);
                f = share.openFile(folder + onlyFileName, new HashSet(
                        Arrays.asList(new AccessMask[]{AccessMask.GENERIC_ALL})),
                        (Set) null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OVERWRITE,
                        (Set) null);
            }
        }
        OutputStream outputStream = null;
        String str = "Hello World";
        if (f != null) {
            outputStream = f.getOutputStream();
            outputStream.write(str.getBytes());
            outputStream.flush();
            outputStream.close();
            System.out.println("写下了文件");
        }
        return true;
    }
}

在controller中调用

@RestController
@RequestMapping("/api/test/share")
public class HelloController {

    @Autowired
    private DiskShareHandleInterface handle;

    @GetMapping("/list")
    public List<String> listDis(String shareName) {
        return handle.listByShareName(shareName);
    }

    @PostMapping("/write")
    public boolean listDis(@RequestBody TestPara testPara) {
        try {
            return handle.writeFile(testPara.getShareName(), testPara.getFilePath());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

}

非常简单的一个demo 记录下,SMB2CreateDisposition里面是些文件操作的枚举。 其操作有如下这些:


import com.hierynomus.protocol.commons.EnumWithValue;

/**
 * [MS-SMB2].pdf 2.2.13 SMB2  CREATE请求 - createDisposition会
 *定义是否是在名称字段中指定的文件已经存在,服务器必须采取的行动。 对于开放的命名管道,该字段可以被设置   				为客户端的任何值,并且必须由服务器忽略。 对于其他文件,该字段必须包含下列值之一。
 */
public enum SMB2CreateDisposition implements EnumWithValue<SMB2CreateDisposition> {
    /**
     * 如果该文件已经存在,它取代。 否则,创建该文件。 此值不应该被用于打印机对象
     */
    FILE_SUPERSEDE(0x00000000L),
    /**
     * 如果该文件已经存在,返回成功; 否则,操作失败。 绝不能用于打印机对象
     */
    FILE_OPEN(0x00000001L),
    /**
     * 如果该文件已经存在,操作失败; 否则,创建该文件。
     */
    FILE_CREATE(0x00000002L),
    /**
     * 打开该文件,如果它已经存在; 否则,创建该文件。 此值不应该被用于打印机对象
     */
    FILE_OPEN_IF(0x00000003L),
    /**
     * 覆盖该文件,如果它已经存在; 否则,操作失败。 绝不能用于打印机对象。
     */
    FILE_OVERWRITE(0x00000004L),
    /**
     * 覆盖该文件,如果它已经存在; 否则,创建该文件。 此值不应该被用于打印机对象。
     */
    FILE_OVERWRITE_IF(0x00000005L);

    private long value;

    SMB2CreateDisposition(long value) {
        this.value = value;
    }

    public long getValue() {
        return value;
    }
}

参考着酌情使用吧。

上次编辑于:
贡献者: tuan,twan