package net.gepafin.tendermanagement.service.impl; import com.amazonaws.AmazonServiceException; import com.amazonaws.SdkClientException; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.*; import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.dao.S3PathConfig; import net.gepafin.tendermanagement.entities.DocumentEntity; import net.gepafin.tendermanagement.entities.UserCompanyDelegationEntity; import net.gepafin.tendermanagement.enums.DocOtherSourceTypeEnum; import net.gepafin.tendermanagement.model.response.UploadFileOnAmazonS3Response; import net.gepafin.tendermanagement.service.AmazonS3Service; import net.gepafin.tendermanagement.util.Utils; import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; import net.gepafin.tendermanagement.web.rest.api.errors.Status; import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Optional; @Service public class AmazonS3ServiceImpl implements AmazonS3Service { private final Logger log = LoggerFactory.getLogger(AmazonS3ServiceImpl.class); @Autowired private AmazonS3 amazonS3; @Autowired private Environment environment; @Value("${aws.s3.bucket.name}") private String bucketName; @Value("${aws.s3.url}") private String s3Url; @Autowired private S3PathConfig s3ConfigBean; @Autowired private AmazonS3Client s3Client; @Value("${aws.s3.region}") private String region; private String getBucketUrlPrefix() { return "https://" + bucketName + ".s3." + region + ".amazonaws.com/"; } private String upload(String fileName, String s3Folder, MultipartFile file) throws IOException { String path = s3Folder +"/"+fileName; InputStream inputStream = file.getInputStream(); ObjectMetadata objectMetadata = new ObjectMetadata(); Map metadata = new HashMap<>(); metadata.put("Content-Type", file.getContentType()); metadata.put("Content-Length", String.valueOf(file.getSize())); Optional> optionalMetaData = Optional.of(metadata); optionalMetaData.ifPresent(map -> { if (!map.isEmpty()) { map.forEach(objectMetadata::addUserMetadata); } }); if(Boolean.FALSE.equals(isTestProfileActivated())) { amazonS3.putObject(bucketName, path, inputStream, objectMetadata); } path =s3Url + s3Folder +"/"+ fileName; log.info("File '{}' uploaded successfully to Amazon S3 with URL: {}", fileName, path); return path; } @Override public Boolean delete(String s3Folder, String filePath) { String fileName = Utils.extractFileName(filePath); String path = s3Folder +"/"+fileName; final DeleteObjectRequest deleteObjectRequest = new DeleteObjectRequest(bucketName, path); if(Boolean.FALSE.equals(isTestProfileActivated())) { amazonS3.deleteObject(deleteObjectRequest); } log.info("File '{}' deleted successfully from Amazon S3", fileName); return true; } public Boolean isTestProfileActivated() { String[] activeProfiles = environment.getActiveProfiles(); return Arrays.stream(activeProfiles).anyMatch("test"::equals); } @Override public InputStream getFile(String s3Folder, String filePath) { try { String fileName = Utils.extractFileName(filePath); String path = s3Folder + "/" + fileName; GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, path); S3Object s3Object = amazonS3.getObject(getObjectRequest); log.info("File fetched successfully from Amazon S3: {}", fileName); return s3Object.getObjectContent(); } catch (AmazonS3Exception e) { log.error("Error occurred while getting file from Amazon S3: {}", e); throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.GET_ERROR_S3)); } } @Override public UploadFileOnAmazonS3Response uploadFileOnAmazonS3(String s3Folder, MultipartFile file) { String extension = FilenameUtils.getExtension(file.getOriginalFilename()); String originalFileName = org.springframework.util.StringUtils.cleanPath(file.getOriginalFilename()); String firstNameContain = originalFileName.substring(0, originalFileName.lastIndexOf('.')); firstNameContain = Utils.replaceSpacesWithUnderscores(firstNameContain); firstNameContain += "_" + Utils.randomKey(7); String fileName = (firstNameContain + "." + extension); try { String filepath = upload(fileName, s3Folder, file); return UploadFileOnAmazonS3Response.builder().fileName(originalFileName).filePath(filepath).build(); } catch (Exception e) { log.error("Error occurred while uploading file from Amazon S3: {}", e); throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.UPLOAD_ERROR_S3)); } } @Override public String generateS3PathForDeletedDocument(DocOtherSourceTypeEnum typeOfDocument, Long callId, Long applicationId,Long amendmentId) { try { return s3ConfigBean.generateDocumentPathForOther(typeOfDocument, callId, applicationId,amendmentId); } catch (IllegalArgumentException e) { throw new CustomValidationException( Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.S3_PATH_GENERATION_ERROR_MSG) ); } } @Override public String generateS3PathForDeletedDocumentForOther() { try { return s3ConfigBean.generateDocumentPathForDelegationAndSignedDocument(DocOtherSourceTypeEnum.DELETED_USER_DELEGATION); } catch (IllegalArgumentException e) { throw new CustomValidationException( Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.S3_PATH_GENERATION_ERROR_MSG) ); } } @Override public void moveFile(String bucketName, String oldPath, String newPath) { try { log.info("Moving file from {} to {} in bucket {}", oldPath, newPath, bucketName); CopyObjectRequest copyRequest = new CopyObjectRequest(bucketName, oldPath, bucketName, newPath); s3Client.copyObject(copyRequest); log.info("File copied successfully from {} to {}", oldPath, newPath); s3Client.deleteObject(bucketName, oldPath); log.info("Original file deleted successfully: {}", oldPath); } catch (AmazonServiceException e) { log.error("AWS service error while moving file: {}", e.getErrorMessage(), e); throw e; } catch (SdkClientException e) { log.error("SDK client error while moving file: {}", e.getMessage(), e); throw e; } catch (Exception e) { log.error("Unexpected error while moving file: {}", e.getMessage(), e); throw e; } } @Override public void deleteDelegationfromS3(UserCompanyDelegationEntity userCompanyDelegationEntity) { String oldS3Path = userCompanyDelegationEntity.getFilePath(); String newS3Path = generateS3PathForDeletedDocumentForOther() + "/" + oldS3Path.substring(oldS3Path.lastIndexOf("/") + 1); String bucketUrlPrefix = getBucketUrlPrefix(); if (oldS3Path.startsWith(bucketUrlPrefix)) { oldS3Path = oldS3Path.replace(bucketUrlPrefix, ""); } moveFile(bucketName, oldS3Path, newS3Path); log.info("File for company ID {} successfully moved to deleted folder.", userCompanyDelegationEntity.getId()); } @Override public void deleteFileFromS3(DocumentEntity documentEntity, Long callId, Long applicationId,Long amendmentId) { try { String oldS3Path = documentEntity.getFilePath(); String newS3Path = generateS3PathForDeletedDocument(DocOtherSourceTypeEnum.valueOf("DELETED_" + documentEntity.getSource().toUpperCase()), callId, applicationId,amendmentId) + "/" + oldS3Path.substring(oldS3Path.lastIndexOf("/") + 1); String bucketUrlPrefix = getBucketUrlPrefix(); if (oldS3Path.startsWith(bucketUrlPrefix)) { oldS3Path = oldS3Path.replace(bucketUrlPrefix, ""); } moveFile(bucketName, oldS3Path, newS3Path); log.info("File for document ID {} successfully moved to deleted folder.", documentEntity.getId()); } catch (Exception e) { log.error("Error moving file for document ID {} to deleted folder: {}", documentEntity.getId(), e.getMessage()); throw new CustomValidationException(Status.VALIDATION_ERROR, "Error occurred while moving file to deleted folder."); } } }