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