001package org.opengion.plugin.cloud; 002 003import java.io.ByteArrayInputStream; 004import java.io.File; 005import java.io.FileFilter; 006import java.io.FileNotFoundException; 007import java.io.IOException; 008import java.io.InputStream; 009import java.util.ArrayList; 010import java.util.List; 011 012import org.apache.commons.lang3.StringUtils; 013import org.opengion.fukurou.model.CloudFileOperation; 014import org.opengion.fukurou.model.FileOperation; 015import org.opengion.fukurou.model.FileOperationInfo; 016import org.opengion.fukurou.util.Closer; 017import org.opengion.fukurou.util.StringUtil; 018import org.opengion.hayabusa.common.HybsSystem; 019import org.opengion.hayabusa.common.HybsSystemException; 020 021import com.amazonaws.auth.AWSCredentials; 022import com.amazonaws.auth.AWSStaticCredentialsProvider; 023import com.amazonaws.auth.BasicAWSCredentials; 024import com.amazonaws.auth.InstanceProfileCredentialsProvider; 025import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration; 026import com.amazonaws.services.s3.AmazonS3; 027import com.amazonaws.services.s3.AmazonS3ClientBuilder; 028import com.amazonaws.services.s3.model.AmazonS3Exception; 029import com.amazonaws.services.s3.model.ListObjectsV2Request; 030import com.amazonaws.services.s3.model.ListObjectsV2Result; 031import com.amazonaws.services.s3.model.ObjectListing; 032import com.amazonaws.services.s3.model.ObjectMetadata; 033import com.amazonaws.services.s3.model.PutObjectRequest; 034import com.amazonaws.services.s3.model.S3Object; 035import com.amazonaws.services.s3.model.S3ObjectSummary; 036 037/** 038 * FileOperation_AWSは、S3ストレージに対して、 039 * ファイル操作を行うクラスです。 040 * 041 * 認証は下記の2通りが可能です。 042 * ・実行サーバのEC2のインスタンスに、S3ストレージのアクセス許可を付与する 043 * ・システムリソースにアクセスキー・シークレットキー・エンドポイント・レギオンを登録する 044 * (CLOUD_STORAGE_S3_ACCESS_KEY、CLOUD_STORAGE_S3_SECRET_KEY、CLOUD_STORAGE_S3_SERVICE_END_POINT、CLOUD_STORAGE_S3_REGION) 045 * 046 * 注意: 047 * バケット名は全ユーザで共有のため、自身のバケット名か、作成されていないバケット名を指定する必要があります。 048 * 049 * @og.rev 5.10.8.0 (2019/02/01) 新規作成 050 * 051 * @version 5 052 * @author oota 053 * @sinse JDK7.0 054 */ 055public class FileOperation_AWS extends CloudFileOperation { 056 /** クラス変数 */ 057 private final AmazonS3 amazonS3; 058 private final String PLUGIN = "aws"; 059 060 /** 061 * コンストラクター 062 * 063 * 初期化処理です。 064 * AWSの認証処理を行います。 065 * 066 * @param bucket バケット 067 * @param inPath パス 068 */ 069 public FileOperation_AWS(String bucket, String inPath) { 070 super(StringUtil.nval(bucket, HybsSystem.sys("CLOUD_BUCKET")), inPath); 071 setPlugin(PLUGIN); 072 073 // アクセスキー 074 final String s3AccessKey = HybsSystem.sys("CLOUD_STORAGE_S3_ACCESS_KEY"); 075 String s3SecretKey = ""; 076 String s3ServiceEndPoint = ""; 077 String s3Region = ""; 078 079 // S3アクセスクライアントの生成 080 if (StringUtils.isEmpty(s3AccessKey)) { 081 // IAMロールによる認証 082 amazonS3 = AmazonS3ClientBuilder.standard() 083 .withCredentials(new InstanceProfileCredentialsProvider(false)) 084 .build(); 085 } else { 086 // リソースのアクセスキーによる認証 087 // シークレットキー 088 s3SecretKey = HybsSystem.sys("CLOUD_STORAGE_S3_SECRET_KEY"); 089 // エンドポイント 090 s3ServiceEndPoint = HybsSystem.sys("CLOUD_STORAGE_S3_SERVICE_END_POINT"); 091 // レギオン 092 s3Region = HybsSystem.sys("CLOUD_STORAGE_S3_REGION"); 093 094 // AWSの認証情報 095 AWSCredentials credentials = new BasicAWSCredentials(s3AccessKey, s3SecretKey); 096 097 // エンドポイント設定 098 EndpointConfiguration endpointConfiguration = new EndpointConfiguration(s3ServiceEndPoint, s3Region); 099 amazonS3 = AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(credentials)) 100 .withEndpointConfiguration(endpointConfiguration) 101 .build(); 102 } 103 104 try { 105 // S3に指定されたバケット(コンテナ)が存在しない場合は、作成する 106 if (!amazonS3.doesBucketExist(conBucket)) { // doesBucketExistV2最新JARだと出ている 107 amazonS3.createBucket(conBucket); 108 } 109 } catch (AmazonS3Exception ase) { 110 StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE); 111 if (StringUtils.isEmpty(s3AccessKey)) { 112 errMsg.append("IAMロールによる認証が失敗しました。"); 113 114 } else { 115 errMsg.append("アクセスキーによる認証が失敗しました。"); 116 errMsg.append(" CLOUD_STORAGE_S3_ACCESS_KEY:").append(s3AccessKey); 117 errMsg.append(" CLOUD_STORAGE_S3_SECRET_KEY:非表示"); 118 errMsg.append(" CLOUD_STORAGE_S3_SERVICE_END_POINT:").append(s3ServiceEndPoint); 119 errMsg.append(" CLOUD_STORAGE_S3_REGION:").append(s3Region); 120 } 121 errMsg.append(" システムエラー情報:").append(ase.getMessage()); 122 throw new HybsSystemException(errMsg.toString()); 123 } 124 } 125 126 /** 127 * 書き込み処理 128 * 129 * InputStreamのデータを書き込みます。 130 * 131 * @param is 書き込みデータのInputStream 132 * @throws IOException ファイル関連エラー情報 133 */ 134 @Override 135 public void write(InputStream is) throws IOException { 136 ByteArrayInputStream bais = null; 137 try { 138 ObjectMetadata om = new ObjectMetadata(); 139 140 byte[] bytes = toByteArray(is); 141 om.setContentLength(bytes.length); 142 bais = new ByteArrayInputStream(bytes); 143 144 PutObjectRequest request = new PutObjectRequest(conBucket, conPath, bais, om); 145 146 amazonS3.putObject(request); 147 } catch (Exception e) { 148 StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE); 149 errMsg.append("AWSバケットに書き込みが失敗しました。path:").append(conPath); 150 errMsg.append(" システムエラー情報:").append(e.getMessage()); 151 throw new IOException(errMsg.toString()); 152 } finally { 153 Closer.ioClose(bais); 154 } 155 } 156 157 /** 158 * 読み込み処理 159 * 160 * データを読み込み、InputStreamとして、返します。 161 * 162 * @return 読み込みデータのInputStream 163 * @throws FileNotFoundException ファイル非存在エラー情報 164 */ 165 @Override 166 public InputStream read() throws FileNotFoundException { 167 S3Object object = null; 168 169 try { 170 object = amazonS3.getObject(conBucket, conPath); 171 } catch (Exception e) { 172 StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE); 173 errMsg.append("AWSバケットから読み込みが失敗しました。path:").append(conPath); 174 errMsg.append(" システムエラー情報:").append(e.getMessage()); 175 throw new FileNotFoundException(errMsg.toString()); 176 } 177 return object.getObjectContent(); 178 } 179 180 /** 181 * 削除処理 182 * 183 * ファイルを削除します。 184 * 185 * @return 成否フラグ 186 */ 187 @Override 188 public boolean delete() { 189 boolean flgRtn = false; 190 191 try { 192 if (isFile()) { 193 // ファイル削除 194 amazonS3.deleteObject(conBucket, conPath); 195 } else if (isDirectory()) { 196 // ディレクトリ削除 197 // 一括削除のapiが無いので、繰り返しで削除を行う 198 ObjectListing objectList = amazonS3.listObjects(conBucket, conPath); 199 List<S3ObjectSummary> list = objectList.getObjectSummaries(); 200 for (S3ObjectSummary obj : list) { 201 amazonS3.deleteObject(conBucket, obj.getKey()); 202 } 203 204 } 205 flgRtn = true; 206 } catch (Exception e) { 207 // エラーはスルーして、falseを返す 208 } 209 210 return flgRtn; 211 } 212 213 /** 214 * コピー処理 215 * 216 * ファイルを指定先に、コピーします。 217 * 218 * @param afPath コピー先 219 * @return 成否フラグ 220 */ 221 @Override 222 public boolean copy(String afPath) { 223 boolean flgRtn = false; 224 225 try { 226 amazonS3.copyObject(conBucket, conPath, conBucket, afPath); 227 flgRtn = true; 228 } catch (Exception e) { 229 // エラーはスルーして、falseを返す 230 } 231 232 return flgRtn; 233 } 234 235 /** 236 * ファイルサイズ取得 237 * 238 * ファイルサイズを返します。 239 * 240 * @return ファイルサイズ 241 */ 242 @Override 243 public long length() { 244 long rtn = 0; 245 246 try { 247 ObjectMetadata meta = amazonS3.getObjectMetadata(conBucket, conPath); 248 rtn = meta.getContentLength(); 249 } catch (Exception e) { 250 // エラーはスルーして、0を返す。 251 } 252 return rtn; 253 } 254 255 /** 256 * 最終更新時刻取得 257 * 258 * 最終更新時刻を取得します。 259 * 260 * @return 最終更新時刻 261 */ 262 @Override 263 public long lastModified() { 264 long rtn = 0; 265 266 try { 267 ObjectMetadata meta = amazonS3.getObjectMetadata(conBucket, conPath); 268 rtn = meta.getLastModified().getTime(); 269 } catch (Exception e) { 270 // エラーはスルーして、0を返す 271 } 272 return rtn; 273 } 274 275 /** 276 * ファイル判定 277 * 278 * ファイルの場合は、trueを返します。 279 * 280 * @return ファイル判定フラグ 281 */ 282 @Override 283 public boolean isFile() { 284 boolean rtn = false; 285 286 if(!isDirectory()) { 287 rtn = amazonS3.doesObjectExist(conBucket, conPath); 288 } 289 290 return rtn; 291 } 292 293 /** 294 * ディレクトリ判定 295 * 296 * ディレクトリの場合は、trueを返します。 297 * 298 * @return ディレクトリ判定フラグ 299 */ 300 @Override 301 public boolean isDirectory() { 302 boolean flgRtn = false; 303 304 if (StringUtils.isEmpty(conPath)) { 305 return true; 306 } 307 308 // S3にはディレクトリの概念はないので、「/」で続くデータが存在するかで、判定 309 ObjectListing objectList = amazonS3.listObjects(conBucket, setDirTail(conPath)); 310 List<S3ObjectSummary> list = objectList.getObjectSummaries(); 311 flgRtn = list.size() == 0 ? false : true; 312 313 return flgRtn; 314 } 315 316 /** 317 * ファイル一覧取得 318 * 319 * パスのファイルとディレクトリ一覧を取得します。 320 * 321 * @param filter フィルタ情報 322 * @return ファイルとティレクトリ一覧 323 */ 324 @Override 325 public File[] listFiles(FileFilter filter) { 326 if (!exists()) { 327 return new FileOperationInfo[0]; 328 } 329 330 String search = conPath; 331 if (isDirectory()) { 332 search = setDirTail(conPath); 333 } 334 335 List<File> rtnList = new ArrayList<File>(); 336 337 // 検索処理 338 ListObjectsV2Request request = new ListObjectsV2Request() 339 .withBucketName(conBucket) 340 .withPrefix(search) 341 .withDelimiter("/"); 342 ListObjectsV2Result list = amazonS3.listObjectsV2(request); 343 List<S3ObjectSummary> objects = list.getObjectSummaries(); 344 345 // ファイル情報の取得 346 for (S3ObjectSummary obj : objects) { 347 String key = obj.getKey(); 348 349 FileOperationInfo file = new FileOperationInfo(PLUGIN, conBucket, key); 350 file.setLastModifiedValue(obj.getLastModified().getTime()); 351 file.setFile(true); 352 file.setSize(obj.getSize()); 353 rtnList.add(file); 354 } 355 356 // ディレクトリ情報の取得 357 List<String> folders = list.getCommonPrefixes(); 358 for (String str : folders) { 359 String key = rTrim(str, '/'); 360 361 FileOperationInfo file = new FileOperationInfo(PLUGIN, conBucket, key); 362 file.setDirectory(true); 363 rtnList.add(file); 364 } 365 366 // フィルタ処理 367 File[] filterList = filter(rtnList, filter); 368 369 return filterList; 370 } 371 372 /** 373 * 親ディレクトリ情報の取得 374 * 375 * 親のディレクトリを返します。 376 * 377 * @return 親のディレクトリ情報 378 */ 379 @Override 380 public FileOperation getParentFile() { 381 return new FileOperation_AWS(conBucket, this.getParent()); 382 } 383 384 /** 以下はローカル環境でのテスト用メソッドです。 */ 385// /** 386// * ローカルでのテスト用コンストラクタ 387// * 388// * 要:super.bucketの値は固定値に変更してください。 389// * 390// * @param inPath 391// */ 392// public FileOperation_AWS(String bucket, String inPath, boolean test) { 393// // super( StringUtil.nval( bucket, HybsSystem.sys("CLOUD_BUCKET") ), inPath); 394// super( StringUtil.nval( bucket, "opengiontestbucket" ), inPath); 395// 396// // aws接続情報 397// String s3AccessKey = "[key]"; 398// String s3SecretKey = "[secret]"; 399// String s3ServiceEndPoint = "s3-ap-northeast-1.amazonaws.com"; 400// String s3Region = "ap-northeast-1"; 401// 402// // proxy環境での設定 403// ClientConfiguration conf = new ClientConfiguration(); 404// conf.setProtocol(Protocol.HTTPS); 405// conf.setProxyHost("mtc-px14"); 406// conf.setProxyPort(8081); 407// 408// // AWSの認証情報 409// AWSCredentials credentials = new BasicAWSCredentials(s3AccessKey, s3SecretKey); 410// 411// // エンドポイント設定 412// EndpointConfiguration endpointConfiguration = new EndpointConfiguration(s3ServiceEndPoint, s3Region); 413// amazonS3 = AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(credentials)) 414// .withEndpointConfiguration(endpointConfiguration) 415// .withClientConfiguration(conf) // テスト用にproxy設定 416// .build(); 417// 418// try { 419// // S3に指定されたバケット(コンテナ)が存在しない場合は、作成する 420// if (!amazonS3.doesBucketExistV2(conBucket)) { 421// amazonS3.createBucket(conBucket); 422// } 423// } catch (AmazonS3Exception ase) { 424// StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE); 425// if (StringUtils.isEmpty(s3AccessKey)) { 426// errMsg.append("IAMロールによる認証か、バケットの作成が失敗しました。"); 427// 428// } else { 429// errMsg.append("アクセスキーによる認証かバケットの作成が失敗しました。"); 430// errMsg.append(" CLOUD_STORAGE_S3_ACCESS_KEY:").append(s3AccessKey); 431// errMsg.append(" CLOUD_STORAGE_S3_SECRET_KEY:非表示"); 432// errMsg.append(" CLOUD_STORAGE_S3_SERVICE_END_POINT:").append(s3ServiceEndPoint); 433// errMsg.append(" CLOUD_STORAGE_S3_REGION:").append(s3Region); 434// } 435// errMsg.append(" バケット名:").append(conBucket); 436// errMsg.append(" システムエラー情報:").append(ase.getMessage()); 437// throw new RuntimeException(errMsg.toString()); 438// } 439// } 440// 441// /** テスト用メソッド */ 442// public static void main(String[] args) { 443//// writeTest(); 444//// listTest(); 445//// methodTest(); 446// copyTest(); 447// } 448// 449// public static void copyTest() { 450// File[] list = new File[] {new File("LIST01.xls"), new File("LIST02.ods")}; 451// 452//// System.out.println(file.exists()); 453// 454// for(File file: list) { 455// FileOperation file2 = new FileOperation_AWS("ootalocaltest", file.getName(), true); 456// FileUtil.copy(file, file2); 457// } 458// } 459// 460// public static void writeTest() { 461// FileOperation file = new FileOperation_AWS("otlocaltest01", "sample/test.txt", true); 462// try(InputStream is = new ByteArrayInputStream("sample".getBytes())){ 463// file.write(is); 464// }catch(Exception e) { 465// System.out.println(e.getMessage()); 466// } 467// 468//// File file 469// } 470// 471// public static void listTest() { 472// File file = new FileOperation_AWS("otlocaltest01", "sample", true); 473// 474// // フィルタ設定 475// HybsFileFilter filter = new HybsFileFilter(); 476// filter.startsWith("te"); 477// 478// File[] list = file.listFiles(filter); 479// 480// System.out.println(list.length); 481// for(File f: list) { 482// FileOperation fo = (FileOperation)f; 483// 484// try(InputStream is = fo.read()){ 485// Files.copy(is, Paths.get("out.txt"), StandardCopyOption.REPLACE_EXISTING); 486// }catch(IOException ioe) { 487// System.out.println(ioe); 488// } 489// } 490// } 491// 492// public static void methodTest() { 493// FileOperation file = new FileOperation_AWS("otlocaltest01", "", true); 494// boolean rtn = false; 495//// rtn = file.isDirectory(); 496// rtn = file.isFile(); 497// 498// System.out.println(rtn); 499// } 500}