ClojureからWindowsの共有ファイル(CIFS)にjcifsを使用してアクセスする

==========

Windowsとの連携で必要となったのでメモ

OSに非依存な方法としてjcifsは今のところ良い方法と思う
win-win間、Linux-win間でのファイルアクセスを確認した

## jcifsの利用
mavenレポジトリに存在するので
(http://mvnrepository.com/artifact/jcifs/jcifs を参照)
leiningenを使ってインストールする。
現時点(2012.4)で最新版は1.3.17なので
project.cljはこんな感じ


``` project.clj
(defproject jcifsample "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.4.0"]
                 [jcifs "1.3.17"]]
  :dev-dependencies [[swank-clojure "1.4.1"]]
  )

```

あとは

```Shell
lein desp
```

を実行

## サンプルコード
### import & useする関数群
 使用するjava.ioまわりとjava.util.Propertiesを指定してimport & useする
 java.ioは必要なものだけを取り込めばOK(サンプルは多めに指定している)
 jcif.smbまわりとjcifs Configを指定する
 jcif.smbも必要なものだけを取り込めばOK


```clojure

(use '(clojure.java.io))
(import
   [java.io FileReader BufferedReader PushbackReader FileInputStream File
    FileOutputStream InputStreamReader PrintWriter OutputStreamWriter]
   [java.util Properties]
   [jcifs.smb SmbFileInputStream SmbFile SmbFileOutputStream]
   [jcifs Config]
   )
```

### CIFSファイル共有の設定

PropertiesにID,PASSをハードコードしているがセキュリティ上はよろしくないので、実際にはセキュリティ要件に合せて実装のこと(実行時にユーザに入力してもらうID,PASSを利用する、ID,PASSを保存する場合は暗号化を施す等)

アクセスするWindowsの指定とテキスト形式で読み込む場合のエンコードを指定

```clojure

;; PropertiesにwindowsでアクセスするユーザID,パスワードを設定
(def prop (Properties.))
(.setProperty prop "jcifs.smb.client.username", "{USER IDを指定}")
(.setProperty prop "jcifs.smb.client.password", "{上記で指定したUSER IDのパスワード}")
(Config/setProperties prop)

;;共有するサーバ、フォルダを指定
(def *test_serv* "smb://{Windowsのコンピュータ名}/{共有フォルダ名}/")

```

以降のサンプルは上記で定義した\*test_serv\*を利用する

### サーバ上あるファイルをテキストとして読む

バイナリとして普通に読み込む場合は
smbFileInputStreamのインスタンスを作成してreadで読み込む

サーバ上ある"新しいテキスト ドキュメント.txt"をテキストとして読む(エンコードはsjis)

```clojure

(->
	(SmbFileInputStream. (str *test_serv* "新しいテキスト ドキュメント.txt"))
    (InputStreamReader. "Shift_JIS")
    (BufferedReader.)
    read-lines)

```

### ファイル一覧の取得

listFilesで共有しているファイル、ディレクトリの一覧をSmbFileオブジェクトとして取得

```clojure
(.listFiles (SmbFile. *test_serv*))
```

listで共有しているファイル、ディレクトリの一覧をファイル名、ディレクトリ名で所得

```clojure
(.list (SmbFile. *test_serv*))
```

ファイルのみを取得する

```clojure
(filter #(.isFile %)
	(.listFiles (SmbFile. *test_serv*)))
```

### ファイルのコピー
ファイルのコピーはclojureのcopy関数が利用できる
ただしアクセス権限の属性はファイルシステムにより異るためコピー出来無い
コピー先のファイルにあわせ再度設定する必要がある

#### リモート-ローカル間の例

リモート-ローカル間のファイルコピーの例(コピー先は実行時のカレントディレクトリ)

```clojure
(with-open [i (SmbFileInputStream. (str *test_serv* "{コピー元ファイル名}"))
		    o (FileOutputStream. (str "./{コピー先ファイル名}"))]
   (copy i o))
```

リモート-ローカル間のファイルの一括コピー例(コピー先は実行時のカレントディレクトリ)
コピー元のフォルダからファイル(隠し属性は除く)のみを一括コピー

```clojure
(for [f
      (filter #(and (.isFile %) (not (.isHidden %)))
              (.listFiles (SmbFile. *test_serv*)))]
  (with-open [i (SmbFileInputStream. f)
              o (FileOutputStream. (str "./" (.getName f)))]
    (copy i o)))
```


#### リモート-リモート間の例

リモートにサーバ上にコピー先のフォルダをtestという名前で作成しこのディレクトリをコピー先に指定する

```clojure
(.mkdir (SmbFile. (str *test_serv* "test/")))
```

以降のサンプルでは上記で作成したtestディレクトリにコピーすることとする

リモート-リモート間のファイルコピーの例

```clojure
(with-open [i (SmbFileInputStream. (str *test_serv* "{コピー元ファイル名}"))
		    o (SmbFileOutputStream. (str *test_serv* "/test/{コピー先ファイル名}"))]
   (copy i o))
```


リモート-リモート間のファイルの一括コピー(リモートのtestフォルダに一括コピー)
コピー元のフォルダからファイル(隠し属性は除く)のみを一括コピー

```clojure
(for [f
      (filter #(and (.isFile %) (not (.isHidden %)))
              (.listFiles (SmbFile. *test_serv*)))]
  (with-open [i (SmbFileInputStream. f)
              o (SmbFileOutputStream. (str *test_serv* "/test/" (.getName f)))]
    (copy i o)))
```