mirror of
https://gitee.com/youlaitech/youlai-mall.git
synced 2024-12-23 05:00:25 +08:00
feat: 图片上传日期路径优化和添加图片上传压缩功能
This commit is contained in:
parent
a6baf6d452
commit
2e8fb1bd19
@ -0,0 +1,54 @@
|
|||||||
|
package com.youlai.common.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件工具类
|
||||||
|
*
|
||||||
|
* @author
|
||||||
|
* @date 2022/7/17
|
||||||
|
*/
|
||||||
|
public class ImgUtils {
|
||||||
|
|
||||||
|
private static final String[] imgSuffixArr = {"bmp", "dib", "gif", "jfif", "jpe", "jpeg", "jpg", "png", "tif", "tiff", "ico"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否图片判断
|
||||||
|
*
|
||||||
|
* @param fileName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean isImg(String fileName) {
|
||||||
|
if (StrUtil.isBlank(fileName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileSuffix = FileUtil.getSuffix(fileName);
|
||||||
|
|
||||||
|
for (String imgSuffix : imgSuffixArr) {
|
||||||
|
if (fileSuffix.equals(imgSuffix)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据图片大小获取压缩比
|
||||||
|
*
|
||||||
|
* @param size 图片大小(单位:Bytes)
|
||||||
|
* @return 压缩比例
|
||||||
|
*/
|
||||||
|
public static float getCompressQuality(long size) {
|
||||||
|
if (size <= 0.1 * 1024 * 1024) {
|
||||||
|
return 0.5f;
|
||||||
|
} else if (size > 0.1 * 1024 * 1024 && size <= 1 * 1024 * 1024) {
|
||||||
|
return 0.3f;
|
||||||
|
} else {
|
||||||
|
// 大于1M
|
||||||
|
return 0.1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,5 +28,11 @@
|
|||||||
<groupId>io.minio</groupId>
|
<groupId>io.minio</groupId>
|
||||||
<artifactId>minio</artifactId>
|
<artifactId>minio</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 图片压缩 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.coobird</groupId>
|
||||||
|
<artifactId>thumbnailator</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
@ -15,7 +15,6 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
@RequestMapping("/api/v1/files")
|
@RequestMapping("/api/v1/files")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class FileController {
|
public class FileController {
|
||||||
|
|
||||||
private final MinioService minioService;
|
private final MinioService minioService;
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@ -23,7 +22,7 @@ public class FileController {
|
|||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public Result<String> uploadFile(
|
public Result<String> uploadFile(
|
||||||
@ApiParam("文件") @RequestParam(value = "file") MultipartFile file,
|
@ApiParam("文件") @RequestParam(value = "file") MultipartFile file,
|
||||||
@ApiParam("存储桶名称(非必须,微服务有单独默认存储桶)") @RequestParam(value = "bucketName", required = false) String bucketName
|
@ApiParam("存储桶名称(没值默认存储桶)") @RequestParam(value = "bucketName", required = false) String bucketName
|
||||||
) {
|
) {
|
||||||
String path = minioService.putObject(file, bucketName);
|
String path = minioService.putObject(file, bucketName);
|
||||||
return Result.success(path);
|
return Result.success(path);
|
||||||
|
@ -1,19 +1,26 @@
|
|||||||
package com.youlai.common.file.service;
|
package com.youlai.common.file.service;
|
||||||
|
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.youlai.common.util.ImgUtils;
|
||||||
import io.minio.*;
|
import io.minio.*;
|
||||||
import io.minio.http.Method;
|
import io.minio.http.Method;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.coobird.thumbnailator.Thumbnails;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.*;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@ -51,6 +58,11 @@ public class MinioService implements InitializingBean {
|
|||||||
@Setter
|
@Setter
|
||||||
private String defaultBucket;
|
private String defaultBucket;
|
||||||
|
|
||||||
|
|
||||||
|
@Value("${minio.img_compression_enabled:false}")
|
||||||
|
private boolean imgCompressionEnabled;
|
||||||
|
|
||||||
|
|
||||||
private MinioClient minioClient;
|
private MinioClient minioClient;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -61,9 +73,7 @@ public class MinioService implements InitializingBean {
|
|||||||
Assert.notBlank(secretKey, "MinIO secretKey不能为空");
|
Assert.notBlank(secretKey, "MinIO secretKey不能为空");
|
||||||
this.minioClient = MinioClient.builder()
|
this.minioClient = MinioClient.builder()
|
||||||
//.endpoint(endpoint, 443, true)
|
//.endpoint(endpoint, 443, true)
|
||||||
.endpoint(endpoint)
|
.endpoint(endpoint).credentials(accessKey, secretKey).build();
|
||||||
.credentials(accessKey, secretKey)
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,19 +83,15 @@ public class MinioService implements InitializingBean {
|
|||||||
*/
|
*/
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public void createBucketIfAbsent(String bucketName) {
|
public void createBucketIfAbsent(String bucketName) {
|
||||||
BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder()
|
BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucketName).build();
|
||||||
.bucket(bucketName)
|
|
||||||
.build();
|
|
||||||
if (!minioClient.bucketExists(bucketExistsArgs)) {
|
if (!minioClient.bucketExists(bucketExistsArgs)) {
|
||||||
MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder()
|
MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();
|
||||||
.bucket(bucketName)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
minioClient.makeBucket(makeBucketArgs);
|
minioClient.makeBucket(makeBucketArgs);
|
||||||
|
|
||||||
// 设置存储桶访问权限为PUBLIC, 如果不配置,则新建的存储桶默认是PRIVATE,则存储桶文件会拒绝访问 Access Denied
|
// 设置存储桶访问权限为PUBLIC, 如果不配置,则新建的存储桶默认是PRIVATE,则存储桶文件会拒绝访问 Access Denied
|
||||||
SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder()
|
SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs
|
||||||
.bucket(bucketName)
|
.builder().bucket(bucketName)
|
||||||
.config(publicBucketPolicy(bucketName).toString())
|
.config(publicBucketPolicy(bucketName).toString())
|
||||||
.build();
|
.build();
|
||||||
minioClient.setBucketPolicy(setBucketPolicyArgs);
|
minioClient.setBucketPolicy(setBucketPolicyArgs);
|
||||||
@ -108,7 +114,7 @@ public class MinioService implements InitializingBean {
|
|||||||
*
|
*
|
||||||
* @param file MultipartFile文件对象
|
* @param file MultipartFile文件对象
|
||||||
* @param bucketName 存储桶名称
|
* @param bucketName 存储桶名称
|
||||||
* @return
|
* @return 上传文件路径
|
||||||
*/
|
*/
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public String putObject(MultipartFile file, String bucketName) {
|
public String putObject(MultipartFile file, String bucketName) {
|
||||||
@ -116,43 +122,61 @@ public class MinioService implements InitializingBean {
|
|||||||
if (StrUtil.isBlank(bucketName)) {
|
if (StrUtil.isBlank(bucketName)) {
|
||||||
bucketName = defaultBucket;
|
bucketName = defaultBucket;
|
||||||
}
|
}
|
||||||
|
// 判断存储桶是否存在
|
||||||
createBucketIfAbsent(bucketName);
|
createBucketIfAbsent(bucketName);
|
||||||
|
|
||||||
String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
|
// 获取文件后缀
|
||||||
String fileName = IdUtil.simpleUUID() + "." + suffix;
|
String suffix = FileUtil.getSuffix(file.getOriginalFilename());
|
||||||
|
// 文件名
|
||||||
|
String uuid = IdUtil.simpleUUID();
|
||||||
|
String fileName = DateUtil.format(LocalDateTime.now(), "yyyy/MM/dd") + "/" + uuid + "." + suffix;
|
||||||
|
|
||||||
InputStream inputStream = file.getInputStream();
|
InputStream inputStream;
|
||||||
|
// 是否开启压缩
|
||||||
|
if (ImgUtils.isImg(fileName) && imgCompressionEnabled) {
|
||||||
|
long fileSize = file.getSize();
|
||||||
|
log.info("图片({})压缩前大小:{}KB", uuid, fileSize / 1024);
|
||||||
|
float compressQuality = ImgUtils.getCompressQuality(fileSize);
|
||||||
|
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(Convert.toInt(fileSize));
|
||||||
|
Thumbnails.of(file.getInputStream())
|
||||||
|
.scale(1f) // 图片大小比例
|
||||||
|
.outputQuality(compressQuality) // 图片质量压缩比
|
||||||
|
.toOutputStream(outputStream);
|
||||||
|
inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||||
|
log.info("图片({})压缩后大小:{}KB", uuid, inputStream.available() / 1024);
|
||||||
|
} else {
|
||||||
|
inputStream = file.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传参数构建
|
||||||
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
|
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
|
||||||
.bucket(bucketName)
|
.bucket(bucketName)
|
||||||
.object(fileName)
|
.object(fileName)
|
||||||
.contentType(file.getContentType())
|
.contentType(file.getContentType())
|
||||||
.stream(inputStream, inputStream.available(), -1)
|
.stream(inputStream, inputStream.available(), -1)
|
||||||
.build();
|
.build();
|
||||||
|
// 上传
|
||||||
minioClient.putObject(putObjectArgs);
|
minioClient.putObject(putObjectArgs);
|
||||||
|
|
||||||
String fileUrl;
|
String fileUrl;
|
||||||
if (StrUtil.isBlank(customDomain)) { // 没有自定义文件路径域名
|
if (StrUtil.isBlank(customDomain)) { // 没有自定义文件路径域名
|
||||||
GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
|
GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
|
||||||
.bucket(bucketName)
|
.bucket(bucketName).object(fileName)
|
||||||
.object(fileName)
|
|
||||||
.method(Method.GET)
|
.method(Method.GET)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
fileUrl = minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs);
|
fileUrl = minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs);
|
||||||
fileUrl = fileUrl.substring(0, fileUrl.indexOf("?"));
|
fileUrl = fileUrl.substring(0, fileUrl.indexOf("?"));
|
||||||
} else { // 自定义文件路径域名,Nginx配置方向代理转发MinIO
|
} else {
|
||||||
fileUrl = customDomain +'/'+ bucketName + "/" + fileName;
|
// 自定义文件路径域名,Nginx配置代理转发
|
||||||
|
fileUrl = customDomain + '/' + bucketName + "/" + fileName;
|
||||||
}
|
}
|
||||||
return fileUrl;
|
return fileUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeObject(String bucket, String fileName) throws Exception {
|
public void removeObject(String bucket, String fileName) throws Exception {
|
||||||
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
|
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(fileName).build();
|
||||||
.bucket(bucket)
|
|
||||||
.object(fileName)
|
|
||||||
.build();
|
|
||||||
minioClient.removeObject(removeObjectArgs);
|
minioClient.removeObject(removeObjectArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,16 +196,17 @@ public class MinioService implements InitializingBean {
|
|||||||
* Action: 操作行为
|
* Action: 操作行为
|
||||||
*/
|
*/
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
builder.append("{\"Version\":\"2012-10-17\"," +
|
builder.append("{\"Version\":\"2012-10-17\","
|
||||||
"\"Statement\":[{\"Effect\":\"Allow\"," +
|
+ "\"Statement\":[{\"Effect\":\"Allow\","
|
||||||
"\"Principal\":{\"AWS\":[\"*\"]}," +
|
+ "\"Principal\":{\"AWS\":[\"*\"]},"
|
||||||
"\"Action\":[\"s3:ListBucketMultipartUploads\",\"s3:GetBucketLocation\",\"s3:ListBucket\"]," +
|
+ "\"Action\":[\"s3:ListBucketMultipartUploads\",\"s3:GetBucketLocation\",\"s3:ListBucket\"],"
|
||||||
"\"Resource\":[\"arn:aws:s3:::" + bucketName + "\"]}," +
|
+ "\"Resource\":[\"arn:aws:s3:::" + bucketName + "\"]},"
|
||||||
"{\"Effect\":\"Allow\"," +
|
+ "{\"Effect\":\"Allow\"," + "\"Principal\":{\"AWS\":[\"*\"]},"
|
||||||
"\"Principal\":{\"AWS\":[\"*\"]}," +
|
+ "\"Action\":[\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:GetObject\"],"
|
||||||
"\"Action\":[\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:GetObject\"]," +
|
+ "\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}");
|
||||||
"\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}");
|
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user