package com.yami.trading.service.impl;
|
|
import cn.hutool.core.util.StrUtil;
|
import com.alibaba.fastjson2.JSONObject;
|
import com.yami.trading.common.constants.Constants;
|
import com.yami.trading.common.exception.BusinessException;
|
import com.yami.trading.common.exception.YamiShopBindException;
|
import com.yami.trading.service.AwsS3OSSFileService;
|
import jodd.util.PropertiesUtil;
|
import lombok.extern.slf4j.Slf4j;
|
import net.coobird.thumbnailator.Thumbnails;
|
import org.apache.commons.io.FilenameUtils;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.stereotype.Service;
|
import org.springframework.web.multipart.MultipartFile;
|
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
|
import software.amazon.awssdk.core.sync.RequestBody;
|
import software.amazon.awssdk.core.waiters.WaiterResponse;
|
import software.amazon.awssdk.regions.Region;
|
import software.amazon.awssdk.services.s3.S3Client;
|
import software.amazon.awssdk.services.s3.model.*;
|
import software.amazon.awssdk.services.s3.waiters.S3Waiter;
|
|
import java.io.*;
|
import java.net.URL;
|
import java.nio.file.Files;
|
import java.time.LocalDate;
|
import java.util.*;
|
|
@Service
|
@Slf4j
|
public class AwsS3OSSFileServiceImpl implements AwsS3OSSFileService {
|
@Value("${oss.aws.s3.bucketName}")
|
private String bucketName;
|
|
@Value("${loca.images.dir}")
|
private String imgDir;
|
|
private final S3Client s3;
|
|
public AwsS3OSSFileServiceImpl(){
|
ProfileCredentialsProvider credentialsProvider = ProfileCredentialsProvider.create();
|
Region region = Region.US_EAST_1;
|
s3 = S3Client.builder()
|
.region(region)
|
.credentialsProvider(credentialsProvider)
|
.build();
|
}
|
//
|
// @Value("${images.dir}")
|
// private String tempFilePath;
|
public static String policyText = "{ \n" +
|
" \"Version\": \"2012-10-17\",\n" +
|
" \"Statement\": [\n" +
|
" {\n" +
|
" \"Effect\": \"Allow\",\n" +
|
" \"Action\": \"s3:*\",\n" +
|
" \"Principal\": \"*\",\n" +
|
" \"Resource\": [\n" +
|
" \"arn:aws:s3:::%s/*\"\n" +
|
" ]\n" +
|
" }\n" +
|
" ]\n" +
|
"}";
|
|
/**
|
* 获取操作客户端
|
*
|
* @return
|
*/
|
private S3Client getS3Client() {
|
return s3;
|
}
|
|
/**
|
* 创建存储桶
|
*
|
* @param bucketName
|
*/
|
public void createBucket(String bucketName) {
|
log.debug("AwsS3OSSFileService createBucket bucketName:{}", bucketName);
|
try {
|
S3Client s3Client = getS3Client();
|
S3Waiter s3Waiter = s3Client.waiter();
|
CreateBucketRequest bucketRequest = CreateBucketRequest.builder()
|
.bucket(bucketName)
|
.build();
|
s3Client.createBucket(bucketRequest);
|
HeadBucketRequest bucketRequestWait = HeadBucketRequest.builder()
|
.bucket(bucketName)
|
.build();
|
WaiterResponse<HeadBucketResponse> r = s3Waiter.waitUntilBucketExists(bucketRequestWait);
|
log.info("AwsS3OSSFileService createBucket result :{}", JSONObject.toJSONString(r));
|
} catch (S3Exception e) {
|
log.error("AwsS3OSSFileService createBucket Exception", e.getMessage(), e.awsErrorDetails().errorMessage(), e);
|
}
|
}
|
|
/**
|
* 设置存储桶策略
|
*
|
* @param bucketName
|
*/
|
public void setPolicy(String bucketName) {
|
log.info("AwsS3OSSFileService setPolicy bucketName:{},policyText:{}", bucketName, policyText);
|
try {
|
S3Client s3Client = getS3Client();
|
PutBucketPolicyRequest policyReq = PutBucketPolicyRequest.builder()
|
.bucket(bucketName)
|
.policy(String.format(policyText, bucketName))
|
.build();
|
PutBucketPolicyResponse r = s3Client.putBucketPolicy(policyReq);
|
log.info("AwsS3OSSFileService setPolicy result :{}", r.toString());
|
log.info("AwsS3OSSFileService setPolicy result :{}", JSONObject.toJSONString(r));
|
} catch (S3Exception e) {
|
log.error("AwsS3OSSFileService setPolicy Exception", e.getMessage(), e.awsErrorDetails().errorMessage(), e);
|
}
|
}
|
|
/**
|
* 获取文件访问链接
|
*
|
* @param keyName
|
*/
|
@Override
|
public String getUrl(String keyName) {
|
if (StrUtil.isEmpty(keyName)) {
|
return null;
|
}
|
if (keyName.startsWith("https")){
|
return keyName;
|
}
|
log.debug("AwsS3OSSFileService getURL bucketName:{},keyName:{}", bucketName, keyName);
|
try {
|
S3Client s3Client = getS3Client();
|
GetUrlRequest request = GetUrlRequest.builder()
|
.bucket(bucketName)
|
.key(keyName)
|
.build();
|
URL url = s3Client.utilities().getUrl(request);
|
if (url != null) {
|
return url.toString();
|
}
|
log.info("The URL for " + keyName + " is " + url);
|
} catch (S3Exception e) {
|
log.error("AwsS3OSSFileService getURL Exception", e.getMessage(), e.awsErrorDetails().errorMessage(), e);
|
}
|
return "";
|
}
|
|
/**
|
* 上传本地文件
|
*
|
* @return
|
*/
|
@Override
|
public String uploadFile(String moduleName, MultipartFile file) {
|
// 兼容c端组件上传原理
|
String originalFilename = file.getOriginalFilename();
|
String fileType = originalFilename != null ? originalFilename : "";
|
if (StrUtil.isEmpty(fileType) || fileType.contains("blob")) {
|
fileType = "blob.png";
|
}
|
|
// 提取文件扩展名
|
String extension = "";
|
int dotIndex = fileType.lastIndexOf('.');
|
if (dotIndex > 0) {
|
extension = fileType.substring(dotIndex);
|
}
|
|
// 生成唯一的文件名
|
String id = UUID.randomUUID().toString();
|
String filename = id + extension;
|
String path = filename; // 直接使用文件名作为路径
|
|
// 确保目标文件夹存在
|
File targetDir = new File(Constants.IMAGES_DIR);
|
if (!targetDir.exists()) {
|
targetDir.mkdirs();
|
// 设置目录权限为755 (rwxr-xr-x)
|
setFilePermissions(targetDir, "755");
|
}
|
|
// 构建本地文件路径
|
File localFile = new File(targetDir, filename);
|
|
// 打印上传路径
|
log.info("LocalFileUploadService uploadFile localFilePath: {}", localFile.getAbsolutePath());
|
|
try {
|
// 将文件保存到本地
|
file.transferTo(localFile);
|
|
// 设置文件权限为644 (rw-r--r--)
|
setFilePermissions(localFile, "644");
|
|
// 如果需要自定义元数据,可以在此处理
|
Map<String, String> metadata = new HashMap<>();
|
metadata.put("x-amz-meta-myVal", "test");
|
|
// 返回文件名
|
return path;
|
|
} catch (IOException e) {
|
log.error("LocalFileUploadService uploadFile IOException", e.getMessage(), e);
|
throw new YamiShopBindException("文件上传失败");
|
}
|
}
|
|
/**
|
* 设置文件或目录权限(使用数字格式)
|
* @param file 文件或目录对象
|
* @param permissions 权限数字字符串,如 "644", "755"
|
*/
|
private void setFilePermissions(File file, String permissions) {
|
try {
|
// 使用数字格式的chmod命令(更可靠)
|
Process process = Runtime.getRuntime().exec(new String[]{"chmod", permissions, file.getAbsolutePath()});
|
int exitCode = process.waitFor();
|
|
// 读取命令输出和错误信息
|
String output = readProcessOutput(process.getInputStream());
|
String error = readProcessOutput(process.getErrorStream());
|
|
if (exitCode != 0) {
|
log.warn("设置文件权限失败,文件: {}, 退出码: {}, 错误: {}",
|
file.getAbsolutePath(), exitCode, error);
|
} else {
|
log.debug("成功设置文件权限 {}: {}", permissions, file.getAbsolutePath());
|
if (!output.isEmpty()) {
|
log.debug("命令输出: {}", output);
|
}
|
}
|
} catch (IOException | InterruptedException e) {
|
log.warn("设置文件权限时发生异常,文件: {}", file.getAbsolutePath(), e);
|
if (e instanceof InterruptedException) {
|
Thread.currentThread().interrupt();
|
}
|
}
|
}
|
|
/**
|
* 读取进程输出流
|
*/
|
private String readProcessOutput(InputStream inputStream) throws IOException {
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
StringBuilder output = new StringBuilder();
|
String line;
|
while ((line = reader.readLine()) != null) {
|
if (output.length() > 0) {
|
output.append("; ");
|
}
|
output.append(line);
|
}
|
return output.toString();
|
}
|
}
|
|
/**
|
* 验证文件权限(调试用)
|
*/
|
private void verifyFilePermissions(File file) {
|
try {
|
Process process = Runtime.getRuntime().exec(new String[]{"ls", "-l", file.getAbsolutePath()});
|
process.waitFor();
|
String output = readProcessOutput(process.getInputStream());
|
log.info("文件权限验证: {}", output);
|
} catch (Exception e) {
|
log.warn("无法验证文件权限: {}", e.getMessage());
|
}
|
}
|
// /**
|
// * 上传本地文件
|
// *
|
// * @return
|
// */
|
// public String putS3Object(String moduleName, MultipartFile file, Float quality) {
|
// String fileType = FilenameUtils.getExtension(file.getOriginalFilename());
|
// String id = UUID.randomUUID().toString();
|
// String path = moduleName + "/" + LocalDate.now() + "/" + id + "." + fileType;
|
// String thumbnailPath = moduleName + "/" + LocalDate.now() + "/" + id + "_thumbnail." + fileType;
|
//
|
// log.info("AwsS3OSSFileService putS3Object bucketName:{},objectKey:{},objectPath:{}", bucketName, path, file.getName());
|
// try {
|
// ByteArrayOutputStream bs = cloneInputStream(file.getInputStream());
|
//
|
// InputStream orgIS = new ByteArrayInputStream(bs.toByteArray());
|
//
|
// Map<String, String> metadata = new HashMap<>();
|
// metadata.put("x-amz-meta-myVal", "test");
|
// PutObjectRequest putOb = PutObjectRequest.builder()
|
// .bucket(bucketName)
|
// .key(path)
|
// .metadata(metadata)
|
// .build();
|
// S3Client s3Client = getS3Client();
|
// s3Client.putObject(putOb, RequestBody.fromInputStream(orgIS, file.getSize()));
|
//
|
// InputStream thumbnailIS = new ByteArrayInputStream(bs.toByteArray());
|
//
|
// File tempFile = new File(tempFilePath + "/temp-img/");
|
// if (!tempFile.exists()) {
|
// tempFile.mkdirs();
|
// }
|
// Thumbnails.of(thumbnailIS).scale(1D).outputQuality(quality).toFile(tempFilePath + "/temp-img/" + id + "_thumbnail." + fileType);
|
//
|
// PutObjectRequest putThumbnailOb = PutObjectRequest.builder()
|
// .bucket(bucketName)
|
// .key(thumbnailPath)
|
// .metadata(metadata)
|
// .build();
|
// File thumbnailFile = new File(tempFilePath + "/temp-img/" + id + "_thumbnail." + fileType);
|
// s3Client.putObject(putThumbnailOb, RequestBody.fromInputStream(Files.newInputStream(thumbnailFile.toPath()), thumbnailFile.length()));
|
// return path;
|
// } catch (S3Exception e) {
|
// log.error("AwsS3OSSFileService putS3Object S3Exception", e.getMessage(), e.awsErrorDetails().errorMessage(), e);
|
// throw new BusinessException("文件上传失败");
|
// } catch (IOException e) {
|
// log.error("AwsS3OSSFileService putS3Object IOException", e.getMessage(), e);
|
// throw new BusinessException("文件上传失败");
|
// }
|
// }
|
|
private static ByteArrayOutputStream cloneInputStream(InputStream input) {
|
try {
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
byte[] buffer = new byte[1024];
|
int len;
|
while ((len = input.read(buffer)) > -1) {
|
baos.write(buffer, 0, len);
|
}
|
baos.flush();
|
return baos;
|
} catch (IOException e) {
|
log.error("cloneInputStream IOException", e.getMessage(), e);
|
}
|
return null;
|
}
|
|
/**
|
* 判断文件是否为图片
|
*
|
* @param fileName
|
* @return
|
*/
|
public boolean isImageFile(String fileName) {
|
List<String> imgTypes = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp");
|
if (StrUtil.isEmpty(fileName)) {
|
return false;
|
}
|
String fileType = FilenameUtils.getExtension(fileName);
|
return imgTypes.contains(fileType.toLowerCase());
|
}
|
}
|