001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.util; 017 018import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 019import java.io.FileInputStream; 020import java.io.FileOutputStream; 021import java.io.InputStream; 022import java.io.OutputStream; 023import java.io.BufferedInputStream; 024import java.io.BufferedOutputStream; 025import java.io.IOException; 026import java.util.Map; 027import java.util.LinkedHashMap ; 028import java.net.MalformedURLException ; 029 030import jcifs.smb.SmbFile; 031 032import org.opengion.fukurou.system.Closer; // 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system 033 034/** 035 * SMBConnect.java は、共通的に使用される Smb関連の基本機能を実装した、クラスです。 036 * 037 * これは、jcifs.smb パッケージをベースに開発されています。 038 * このクラスの実行には、jcifs-1.3.14.jar が必要です。 039 * 040 * 接続先のURL schemeは、以下の形式をしています。 041 * smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?[param=value[param2=value2[...]]] 042 * このURLは、内部的に自動作成します。/[[share/[dir/]file]] 部分が、remoteFile として指定する部分になります。 * 043 * 044 * -host=Smbサーバー -user=ユーザー -passwd=パスワード -remoteFile=Smb先のファイル名 を必須設定します。 045 * -localFile=ローカルのファイル名は、必須ではありませんが、-command=DEL の場合にのみ不要であり、 046 * それ以外の command の場合は、必要です。 047 * 048 * -command=[GET/PUT/DEL/GETDIR/PUTDIR/DELDIR] は、SFTPサーバーに対しての処理の方法を指定します。 049 * GET:Smbサーバーからローカルにファイル転送します(初期値) 050 * PUT:ローカルファイルをSmbサーバーに PUT(STORE、SAVE、UPLOAD、などと同意語)します。 051 * DEL:Smbサーバーの指定のファイルを削除します。この場合のみ、-localFile 属性の指定は不要です。 052 * GETDIR,PUTDIR,DELDIR:指定のフォルダ以下のファイルを処理します。 053 * 054 * -mkdirs=[true/false] は、受け側のファイル(GET時:LOCAL、PUT時:Smbサーバー)に取り込むファイルのディレクトリが 055 * 存在しない場合に、作成するかどうかを指定します(初期値:true)。 056 * 通常、Smbサーバーに、フォルダ階層を作成してPUTする場合、動的にフォルダ階層を作成したいケースで便利です。 057 * 逆に、フォルダは確定しており、指定フォルダ以外に PUT するのはバグっていると事が分かっている場合には 058 * false に設定して、存在しないフォルダにPUT しようとすると、エラーになるようにします。 059 * 060 * 引数文字列中に空白を含む場合は、ダブルコーテーション("") で括って下さい。 061 * 引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に 062 * 繋げてください。 063 * 064 * @og.formSample 065 * SMBConnect -host=Smbサーバー -user=ユーザー -passwd=パスワード -remoteFile=Smb先のファイル名 [-localFile=ローカルのファイル名] 066 * [-command=[GET/PUT/DEL/GETDIR/PUTDIR/DELDIR] ] ] 067 * 068 * -host=Smbサーバー :接続先のSmbサーバーのアドレスまたは、サーバー名 069 * -user=ユーザー :接続するユーザー名 070 * -passwd=パスワード :接続するユーザーのパスワード 071 * -remoteFile=Smb先のファイル名 :接続先のSmbサーバー側のファイル名。PUT,GET 関係なくSmb側として指定します。 072 * [-localFile=ローカルのファイル名] :ローカルのファイル名。PUT,GET 関係なくローカルファイルを指定します。 073 * [-domain=ドメイン ] :接続するサーバーのドメインを指定します。 074 * [-port=ポート ] :接続するサーバーのポートを指定します。 075 * [-command=[GET/PUT/DEL] ] :Smbサーバー側での処理の方法を指定します。 076 * [GETDIR/PUTDIR/DELDIR]] GET:Smb⇒LOCAL、PUT:LOCAL⇒Smb への転送です(初期値:GET) 077 * DEL:Smbファイルを削除します。 078 * GETDIR,PUTDIR,DELDIR 指定のフォルダ以下のファイルを処理します。 079 * [-mkdirs=[true/false] ] :受け側ファイル(GET時:LOCAL、PUT時:Smbサーバー)にディレクトリを作成するかどうか(初期値:true) 080 * (false:ディレクトリが無ければ、エラーにします。) 081 * [-display=[false/true] ] :trueは、検索状況を表示します(初期値:false) 082 * [-debug=[false|true] ] :デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない]) 083 * 084 * @og.rev 5.1.6.0 (2010/05/01) 新規追加 085 * 086 * @version 5.0 087 * @author Kazuhiko Hasegawa 088 * @since JDK5.0, 089 */ 090public final class SMBConnect extends AbstractConnect { 091 private String domain ; // ドメイン 092 private String connURI ; // Smb接続する場合のURI文字列(の先頭) 093 094 /** 095 * Smbサーバーへの接続、ログインを行います。 096 * 097 * このメソッドは、初期化メソッドです。 098 * Smbサーバーへの接続、ログインを行いますので、複数ファイルを転送する 099 * ケースでは、最初に1度だけ呼び出すだけです。 100 * 接続先を変更する場合は、もう一度このメソッドをコールする必要があります。 101 * (そのような場合は、通常、オブジェクトを構築しなおす方がよいと思います。) 102 * 接続時のアドレスは、下記の形式になりますが、ファイル名の箇所は、action 時に指定するため、 103 * ここでは、先頭部分を先に用意しておきます。 104 * smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?[param=value[param2=value2[...]]] 105 * 106 */ 107 @Override // AbstractConnect#ConnectIF 108 public void connect() { 109 if( isDisplay ) { System.out.println( "CONNECT: HOST=" + host + ",USER=" + user + ",PORT=" + port ); } 110 111 if( host == null ) { 112 errAppend( "host は、必須です。" ); 113 throw new OgRuntimeException( getErrMsg() ); 114 } 115 116 // smb://[[[domain;]username[:password]@]server[:port] ここまで作成 117 connURI = "smb://" 118 + ((domain == null) ? "" : domain + ";" ) 119 + ((user == null) ? "" : user ) 120 + ((passwd == null) ? "" : ":" + passwd ) 121 + ((user == null) ? "" : "@" ) 122 + host 123 + ((port == null) ? "" : ":" + port ) ; 124 125 if( isDebug ) { System.out.println( "connURI=" + connURI ); } 126 } 127 128 /** 129 * Smbサーバーとの接続をクローズします。 130 * 131 * ここでは、何も処理を行いません。 132 * 133 */ 134 @Override // AbstractConnect#ConnectIF 135 public void disconnect() { 136 if( isDisplay ) { System.out.println( "DISCONNECT:" ); } 137 } 138 139 /** 140 * command="GET" が指定されたときの処理を行います。 141 * 142 * 接続先のSmbサーバー側のファイル名をローカルにダウンロードします。 143 * 144 * @param localFile ローカルのファイル名 145 * @param remoteFile Smb先のファイル名 146 * @throws IOException 入出力エラーが発生したとき 147 */ 148 @Override // AbstractConnect 149 protected void actionGET( final String localFile, final String remoteFile ) throws IOException { 150 if( isDebug ) { System.out.println( "GET: " + remoteFile + " => " + localFile ); } 151 152 final SmbFile rmtFile = makeSmbURI( remoteFile ); 153 154 // GET(DOWNLOAD)取得時は、ローカルファイルのディレクトリを作成する必要がある。 155 if( isMkdirs ) { 156 makeLocalDir( localFile ); 157 } 158 159 InputStream input = null; 160 OutputStream output = null; 161 try { 162 input = new BufferedInputStream( rmtFile.getInputStream() ); 163 output = new FileOutputStream( localFile ); 164 FileUtil.copy( input,output ); 165 } 166 finally { 167 Closer.ioClose( input ); 168 Closer.ioClose( output ); 169 } 170 } 171 172 /** 173 * command="GETDIR" が指定されたときの処理を行います。 174 * 175 * 接続先のSmbサーバー側のディレクトリ以下をローカルディレクトリに階層構造のままダウンロードします。 176 * 177 * @param localDir ローカルのディレクトリ名 178 * @param remoteDir Smb先のディレクトリ名 179 * @throws IOException 入出力エラーが発生したとき 180 */ 181 @Override // AbstractConnect 182 protected void actionGETdir( final String localDir, final String remoteDir ) throws IOException { 183 final SmbFile rmtFile = makeSmbURI( remoteDir ); 184 185 final SmbFile[] rmtFiles = rmtFile.listFiles(); 186 for( int i=0; i<rmtFiles.length; i++ ) { 187 final String rmt = rmtFiles[i].getName(); 188 if( rmtFiles[i].isDirectory() ) { 189 actionGETdir( addFile( localDir,rmt ),addFile( remoteDir,rmt ) ); 190 } 191 else { 192 actionGET( addFile( localDir,rmt ),addFile( remoteDir,rmt ) ); 193 } 194 } 195 } 196 197 /** 198 * command="PUT" が指定されたときの処理を行います。 199 * 200 * ローカルファイルを、接続先のSmbサーバー側にアップロードします。 201 * 202 * @param localFile ローカルのファイル名 203 * @param remoteFile Smb先のファイル名 204 * @throws IOException 入出力エラーが発生したとき 205 */ 206 @Override // AbstractConnect 207 protected void actionPUT( final String localFile, final String remoteFile ) throws IOException { 208 if( isDebug ) { System.out.println( "PUT: " + localFile + " => " + remoteFile ); } 209 210 final SmbFile rmtFile = makeSmbURI( remoteFile ); 211 212 // 存在チェック:すでに存在している場合は、先に削除します。 213 if( rmtFile.exists() ) { rmtFile.delete() ; } 214 else { 215 // PUT(UPLOAD)登録時は、リモートファイルのディレクトリを作成する必要がある。 216 if( isMkdirs ) { 217 final String tmp = rmtFile.getParent(); 218 final SmbFile dir = new SmbFile( tmp ); 219 if( !dir.exists() ) { dir.mkdirs() ; } 220 } 221 } 222 223 InputStream input = null; 224 OutputStream output = null; 225 try { 226 input = new FileInputStream( localFile ); 227 output = new BufferedOutputStream( rmtFile.getOutputStream() ); 228 229 FileUtil.copy( input,output ); 230 } 231 finally { 232 Closer.ioClose( input ); 233 Closer.ioClose( output ); 234 } 235 } 236 237 /** 238 * command="DEL" が指定されたときの処理を行います。 239 * 240 * 接続先のSmbサーバー側のファイル名を削除します。 241 * 242 * @param remoteFile Smb先のファイル名 243 * @throws IOException 入出力エラーが発生したとき 244 */ 245 @Override // AbstractConnect 246 protected void actionDEL( final String remoteFile ) throws IOException { 247 if( isDebug ) { System.out.println( "DEL: " + remoteFile ); } 248 249 final SmbFile rmtFile = makeSmbURI( remoteFile ); 250 rmtFile.delete() ; 251 } 252 253 /** 254 * command="DELDIR" が指定されたときの処理を行います。 255 * 256 * 接続先のSmbサーバー側のディレクトリ以下をローカルディレクトリに階層構造のままダウンロードします。 257 * 258 * @param remoteDir Smb先のディレクトリ名 259 * @throws IOException 入出力エラーが発生したとき 260 */ 261 @Override // AbstractConnect 262 protected void actionDELdir( final String remoteDir ) throws IOException { 263 final SmbFile rmtFile = makeSmbURI( remoteDir ); 264 final SmbFile[] rmtFiles = rmtFile.listFiles(); 265 for( int i=0; i<rmtFiles.length; i++ ) { 266 final String rmt = addFile( remoteDir,rmtFiles[i].getName() ); 267 if( rmtFiles[i].isDirectory() ) { 268 actionDELdir( rmt ); 269 } 270 else { 271 actionDEL( rmt ); 272 } 273 } 274 rmtFile.delete(); 275 } 276 277 /** 278 * SMB形式のURLを作成します。 279 * 280 * 処理的には、内部で先に作成済みの connURI と、引数のファイルパスを文字列連結します。 281 * connURI の最後には、"/" を付加していませんので、引数のファイルパスに、 282 * "/" を含まない場合は、"/" を付加します。 283 * 284 * smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]] 285 * 286 * @param remoteFile Smb先のファイル名 287 * 288 * @return SMB形式のURL 289 * @og.rtnNotNull 290 */ 291 private SmbFile makeSmbURI( final String remoteFile ) throws MalformedURLException { 292 final String smbFile ; 293 294 if( remoteFile.startsWith( "smb://" ) ) { 295 smbFile = remoteFile; 296 } 297 else if( StringUtil.startsChar( remoteFile , '/' ) ) { // 6.2.0.0 (2015/02/27) 1文字 String.startsWith 298 smbFile = connURI + remoteFile ; 299 } 300 else { 301 smbFile = connURI + "/" + remoteFile ; 302 } 303 304 return new SmbFile( smbFile ); 305 } 306 307 /** 308 * 接続先にログインするドメインを設定します。 309 * 310 * @param domain 接続先にログインするドメイン 311 */ 312 public void setDomain( final String domain ) { 313 this.domain = domain ; 314 } 315 316 /** 317 * このクラスの動作確認用の、main メソッドです。 318 * 319 * @param args コマンド引数配列 320 */ 321 public static void main( final String[] args ) { 322 323 final String[] CMD_LST = new String[] { "GET","PUT","DEL","GETDIR","PUTDIR","DELDIR" }; 324 325 final Map<String,String> mustProparty ; // [プロパティ]必須チェック用 Map 326 final Map<String,String> usableProparty ; // [プロパティ]整合性チェック Map 327 328 mustProparty = new LinkedHashMap<>(); 329 mustProparty.put( "host", "接続先のSmbサーバーのアドレスまたは、サーバー名(必須)" ); 330 mustProparty.put( "user", "接続するユーザー名(必須)" ); 331 mustProparty.put( "passwd", "接続するユーザーのパスワード(必須)" ); 332 mustProparty.put( "command", "Smbサーバー側での処理の方法(GET/PUT/DEL/GETDIR/PUTDIR/DELDIR)を指定します。(必須)" ); 333 mustProparty.put( "remoteFile", "接続先のSmbサーバー側のファイル名(必須)" ); 334 335 usableProparty = new LinkedHashMap<>(); 336 usableProparty.put( "localFile", "ローカルのファイル名" ); 337 usableProparty.put( "domain", "接続先にログインするドメインを指定します。" ); 338 usableProparty.put( "port", "接続に利用するポート番号を設定します。" ); 339 usableProparty.put( "mkdirs", "受け側ファイル(GET時:LOCAL、PUT時:Smbサーバー)にディレクトリを作成するかどうか(初期値:true)" ); 340 usableProparty.put( "display", "[false/true]:trueは、検索状況を表示します(初期値:false)" ); 341 usableProparty.put( "debug", "デバッグ情報を標準出力に表示する(true)かしない(false)か" + 342 CR + "(初期値:false:表示しない)" ); 343 344 // ******************************************************************************************************* // 345 // 以下、単独で使用する場合の main処理 346 // ******************************************************************************************************* // 347 final Argument arg = new Argument( "org.opengion.fukurou.util.SMBConnect" ); 348 arg.setMustProparty( mustProparty ); 349 arg.setUsableProparty( usableProparty ); 350 arg.setArgument( args ); 351 352 final SMBConnect smb = new SMBConnect(); 353 354 final String host = arg.getProparty( "host"); // Smbサーバー 355 final String user = arg.getProparty( "user" ); // ユーザー 356 final String passwd = arg.getProparty( "passwd" ); // パスワード 357 358 smb.setHostUserPass( host,user,passwd ); 359 360 smb.setDomain( arg.getProparty( "domain" ) ); // 接続先にログインするドメインを指定します。 361 smb.setPort( arg.getProparty( "port" ) ); // 接続に利用するポート番号を設定します。 362 smb.setMkdirs( arg.getProparty( "mkdirs" ,true ) ); // 受け側ファイルにディレクトリを作成するかどうか 363 smb.setDisplay( arg.getProparty( "display" ,false ) ); // trueは、検索状況を表示します(初期値:false) 364 smb.setDebug( arg.getProparty( "debug" ,false ) ); // デバッグ情報を標準出力に表示する(true)かしない(false)か 365 366 try { 367 // コネクトします。 368 smb.connect(); 369 370 final String command = arg.getProparty( "command" ,"GET" ,CMD_LST ); // Smb処理の方法を指定します。 371 final String localFile = arg.getProparty( "localFile" ); // ローカルのファイル名 372 final String remoteFile = arg.getProparty( "remoteFile" ); // Smb先のファイル名 373 374 // command , localFile , remoteFile を元に、SFTP処理を行います。 375 smb.action( command,localFile,remoteFile ); 376 } 377 catch( final RuntimeException ex ) { 378 System.err.println( smb.getErrMsg() ); 379 } 380 finally { 381 // ホストとの接続を終了します。 382 smb.disconnect(); 383 } 384 } 385}