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