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