Smbjの使い方 まとめ | Java

※当サイトはアフィリエイト広告を利用しています。

この記事ではSMB 2.0/3.0でファイルサーバーにアクセスするためのJavaライブラリであるSmbjの使い方をまとめます。
スポンサーリンク


Smbjとは?

SmbjとはSMB 2.0/3.0のファイルサーバーにサクセスするためのJavaライブラリです。
このライブラリを使用することで非常に簡単にSMBファイルサーバーへのアクセスが可能となります。

導入方法は?

Smbjはmaven repositoryに公開されているため、mavenやgradleなどで簡単に導入することができます。

ライセンスは?

Apache 2.0です。そのため、営利目的なソフトであっても使用できますし、ソースコードの開示義務もありません。

SmbjとjCIFSとの違いは?

SMBサーバーにアクセスするJavaライブラリとして他に有名なものとしてはjCIFSがあります。

SmbjとjCIFSの違いをまとめると以下になります。
SmbjjCIFS
対応SMBバージョン2.0 / 3.01.0
ライセンスApache 2.0LGPL 2.1
共有フォルダリストの取得不可
SmbjとjCIFSの比較表
それぞれの詳細を以下に記載します。

対応SMBバージョン

SmbjはSMB 2.0/3.0のみに対応し、jCIFSはSMB 1.0のみに対応しています。

最近のWindowsにおいては、SMB 2.0/3.0がデフォルトで有効なっており、一方でSMB 1.0は無効になっています。
よって、Smbjでプログラムを作成した方がSMBバージョンの設定が不要でクレームなどが来づらくなるかもしれません。

ライセンス

SmbjはApache 2.0なので比較的ライセンスが緩く、Apache 2.0のルールに則っていればそこまでライセンスにそこまで気を配る必要はありません。

一方でjCIFSはLGPL 2.1であり、LGPL 2.1は非常にJavaと相性が悪いので注意が必要です。

具体的にはLGPL 2.1はソースコードの開示を求められた場合に開示する義務があります。
例外としてはdllなどの動的リンクであれば開示する必要はないのですが、Javaの通常のライブラリインポートですとソースコードをそのままインポートするだけで動的リンクでないため、開示を求められた場合には開示する必要が生じてしまいます。

共有フォルダリストの取得

jCIFSは共有フォルダリストが取得可能です。

一方でSmbjは共有フォルダのリストが取得できないので、IPアドレスなどの情報に加えて、共有フォルダ名を指定してあげる必要があり、すこし不便です。

ファイルリストを取得する

「\192.168.0.1\xxx\yyy」フォルダにあるファイルリストを取得するサンプルコードです。
String IP_ADDRESS = "192.168.0.1";
String USERNAME = "Username";
String PASSWORD = "Password";
String SHARED_DIR_NAME = "xxx";
String PATH = "yyy";

ArrayList<FileIdBothDirectoryInformation> result = new ArrayList<>();

try(SMBClient client = new SMBClient()) {
    try{
        //接続を確立する
        Connection connection = client.connect(IP_ADDRESS);

        //認証を通す
        AuthenticationContext ac = new AuthenticationContext(USERNAME, PASSWORD.toCharArray(), "");
        Session session = connection.authenticate(ac);

        //アクセスのためのオブジェクトを取得する
        DiskShare ds = (DiskShare) session.connectShare(SHARED_DIR_NAME);
        
        //FileIdBothDirectoryInformationのリストを取得する
        List<FileIdBothDirectoryInformation> list = ds.list(PATH);

        //ファイルのパスリストに変換する
        Collections.sort(list, (a, b) -> a.getFileName().compareTo(b.getFileName()));
        for(FileIdBothDirectoryInformation f : list) {
            if(f.getFileName().equals(".") || f.getFileName().equals("..") ) continue; //親フォルダへのリンクは無視する
            result.add(f);
        }

    } catch (Exception e) {
        //例外発生時
    }
}
基本的な内容はコメントで記載した通りです。

このサンプルコードではパスの文字列リストを結果として作成しています。
DiskShare.listの結果には「.」と「..」という名前の親フォルダへのリンクが含まれるので結果作成時には除外しています。

ファイルを読み込む(ファイルのByte配列を取得する)

「\192.168.0.1\xxx\yyy\zzz.bmp」というファイルのByte配列を取得するサンプルコードです。
String IP_ADDRESS = "192.168.0.1";
String USERNAME = "Username";
String PASSWORD = "Password";
String SHARED_DIR_NAME = "xxx";
String PATH = "yyy\\zzz.bmp";

Set<AccessMask> AccessMaskSet = new HashSet<>();
Set<FileAttributes> FileAttributesSet = new HashSet<>();
Set<SMB2ShareAccess> SMB2ShareAccessSet;
SMB2CreateDisposition smb2CreateDisposition;
Set<SMB2CreateOptions> Smb2CreateOptionsSet = new HashSet<>();

int GET_BYTE_BUFFER_SIZE = 1024;
byte[] result;

try(SMBClient client = new SMBClient()) {
    try{
        //接続を確立する
        Connection connection = client.connect(IP_ADDRESS);

        //認証を通す
        AuthenticationContext ac = new AuthenticationContext(USERNAME, PASSWORD.toCharArray(), "");
        Session session = connection.authenticate(ac);

        //アクセスのためのオブジェクトを取得する
        DiskShare ds = (DiskShare) session.connectShare(SHARED_DIR_NAME);

        //アクセス条件を設定する
        AccessMaskSet.add(AccessMask.GENERIC_ALL);
        FileAttributesSet.add(FileAttributes.FILE_ATTRIBUTE_NORMAL);
        SMB2ShareAccessSet = SMB2ShareAccess.ALL;
        smb2CreateDisposition = SMB2CreateDisposition.FILE_OPEN;
        Smb2CreateOptionsSet.add(SMB2CreateOptions.FILE_RANDOM_ACCESS);
        
        //ファイルのInputStreamを取得する
        File file = ds.openFile(
                            PATH,
                            AccessMaskSet,
                            FileAttributesSet,
                            SMB2ShareAccessSet,
                            smb2CreateDisposition,
                            Smb2CreateOptionsSet
                    );
        InputStream is = file.getInputStream();

        //Byte配列に変換する
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        int length;
        byte[] buffer = new byte[GET_BYTE_BUFFER_SIZE];
        while((length = is.read(buffer)) != -1) {
            if(requestKillThread) return;
            bos.write(buffer, 0, length);
        }
        result = bos.toByteArray()

    } catch (Exception e) {
        //例外発生時
    }
}
DiskShareオブジェクト取得まではさきほどのファイルパスリスト取得と同じです。

その後はDiaskShare.openFileメソッドを使用してSmbjのFileオブジェクトを取得します。 AccessMaskなどの意味はこちらのドキュメントを参照してください。

Fileオブジェクト取得後はInputStreamを取得し、それをbyte配列に変換します。

注意点

当たり前ですが、SMBサーバーへのアクセスへは一定の時間がかかります。

よって、メインスレッドでsmbjのコードを実行するとその間はプログラムが停止してしまいます。

そのため、別スレッドを立ててそこでsmbjのコードを記述するようにしましょう。

まとめ

この記事ではSMB 2.0/3.0でファイルサーバーにアクセスするためのJavaライブラリであるSmbjの使い方をまとめました。