|
|
@@ -12,6 +12,7 @@ import com.jzg.commons.util.idgen.IdGenerate;
|
|
|
import com.jzg.mapper.SysUploadFilesMapper;
|
|
|
import io.minio.*;
|
|
|
import io.swagger.v3.oas.annotations.Operation;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
@@ -31,6 +32,7 @@ import java.time.LocalDateTime;
|
|
|
|
|
|
@RestController
|
|
|
@RequestMapping("/file")
|
|
|
+@Slf4j
|
|
|
public class FileUploadController extends BaseController {
|
|
|
|
|
|
|
|
|
@@ -57,42 +59,72 @@ public class FileUploadController extends BaseController {
|
|
|
public HttpResult uploadFile(@RequestParam("file") MultipartFile file) {
|
|
|
System.out.printf("调用文件上传接口");
|
|
|
try {
|
|
|
- // 获取文件原始名称和扩展名
|
|
|
+ // 获取文件原始名称,用于后续生成对象名
|
|
|
String originalFileName = file.getOriginalFilename();
|
|
|
+ // 获取文件扩展名(含点号,如 ".jpg"),用于判断文件类型和生成对象名
|
|
|
String extension = getFileExtension(originalFileName);
|
|
|
+ // 获取文件原始大小,用于压缩效果对比
|
|
|
long originalSize = file.getSize();
|
|
|
+ // 获取文件的 Content-Type,用于判断是否为图片类型
|
|
|
+ String contentType = file.getContentType();
|
|
|
|
|
|
- // 判断是否为图片文件,若是则进行压缩
|
|
|
- // 双重校验:通过 Content-Type 和文件扩展名判断
|
|
|
+ // 压缩后的字节数组,初始为null表示未压缩或压缩失败
|
|
|
byte[] compressedBytes = null;
|
|
|
- if (ImageUtil.isImage(file.getContentType()) || ImageUtil.isImageExtension(extension)) {
|
|
|
+ // 压缩后的文件扩展名,默认使用原扩展名,PNG转JPEG时会更新
|
|
|
+ String compressedExtension = extension;
|
|
|
+ // 压缩后的 Content-Type,默认使用原类型,PNG转JPEG时会更新
|
|
|
+ String compressedContentType = contentType;
|
|
|
+
|
|
|
+ // 判断是否为图片文件(双重校验:Content-Type 和扩展名)
|
|
|
+ // 图片文件需要进行压缩处理,非图片文件直接上传
|
|
|
+ if (ImageUtil.isImage(contentType) || ImageUtil.isImageExtension(extension)) {
|
|
|
compressedBytes = ImageUtil.compressImage(file);
|
|
|
}
|
|
|
|
|
|
+ // 检查压缩结果:压缩成功且压缩后文件不为空
|
|
|
if (compressedBytes != null && compressedBytes.length > 0) {
|
|
|
- System.out.printf("图片压缩前大小: %d bytes, 压缩后大小: %d bytes%n", originalSize, compressedBytes.length);
|
|
|
+ // 打印压缩前后大小对比,便于调试和监控压缩效果
|
|
|
+ log.info("图片压缩前大小: {} bytes, 压缩后大小: {} bytes", originalSize, compressedBytes.length);
|
|
|
|
|
|
+ // 判断压缩是否有效:如果压缩后文件比原图大或相等,则视为压缩无效
|
|
|
+ // 此时回退到使用原文件上传,避免上传更大的文件
|
|
|
if (compressedBytes.length >= originalSize) {
|
|
|
- System.out.printf("警告:压缩后图片(%d bytes)比原图(%d bytes)大,使用原图片上传%n", compressedBytes.length, originalSize);
|
|
|
+ log.info("警告:压缩后图片({} bytes)比原图{} bytes)大,使用原图片上传", compressedBytes.length, originalSize);
|
|
|
compressedBytes = null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- String fileMd5;
|
|
|
- String fileEtag;
|
|
|
- String objectName;
|
|
|
+ // 声明文件哈希值和对象名变量
|
|
|
+ String fileMd5; // 文件MD5值,用于唯一标识文件
|
|
|
+ String fileEtag; // 文件ETag值,用于MinIO文件校验
|
|
|
+ String objectName; // MinIO中的对象名称
|
|
|
|
|
|
+ // 根据是否压缩成功,选择不同的处理路径
|
|
|
if (compressedBytes != null && compressedBytes.length > 0) {
|
|
|
- // 生成对象名
|
|
|
+ // 压缩成功:基于压缩后的字节数组计算哈希值
|
|
|
fileMd5 = FileUtils.getFileMD5(compressedBytes);
|
|
|
fileEtag = FileUtils.getFileETag(compressedBytes);
|
|
|
- objectName = generateObjectName(originalFileName, fileMd5, extension);
|
|
|
+
|
|
|
+ // 特殊处理:当原文件是PNG格式,但压缩后实际是JPEG格式时
|
|
|
+ // 需要更新扩展名和Content-Type,确保文件类型与内容一致
|
|
|
+ // 这是因为ImageUtil在PNG压缩无效时会尝试转为JPEG格式
|
|
|
+ if (".png".equalsIgnoreCase(extension) && isJpegContent(compressedBytes)) {
|
|
|
+ compressedExtension = ".jpg";
|
|
|
+ compressedContentType = "image/jpeg";
|
|
|
+ log.info("PNG图片转换为JPEG格式,扩展名从 {} 变为 {}", extension, compressedExtension);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用压缩后的扩展名生成MinIO对象名
|
|
|
+ objectName = generateObjectName(originalFileName, fileMd5, compressedExtension);
|
|
|
} else {
|
|
|
+ // 压缩失败或非图片文件:基于原文件计算哈希值
|
|
|
fileMd5 = FileUtils.getFileMD5(file);
|
|
|
fileEtag = FileUtils.getFileETag(file);
|
|
|
+ // 使用原扩展名生成MinIO对象名
|
|
|
objectName = generateObjectName(originalFileName, fileMd5, extension);
|
|
|
}
|
|
|
|
|
|
+// // 检查文件是否已存在于MinIO(功能已注释,如需启用请取消注释)
|
|
|
// if (isFileExistsInMinIo(objectName, fileEtag)) {
|
|
|
// return HttpResult.ok(getFileFromDatabase(fileEtag,objectName, extension));
|
|
|
// }
|
|
|
@@ -100,8 +132,10 @@ public class FileUploadController extends BaseController {
|
|
|
// 根据是否压缩成功,选择对应的上传方式
|
|
|
SysUploadFiles sysUploadFiles;
|
|
|
if (compressedBytes != null && compressedBytes.length > 0) {
|
|
|
- sysUploadFiles = uploadFileToMinIo(compressedBytes, objectName, fileEtag, extension, file.getContentType());
|
|
|
+ // 上传压缩后的图片字节数组(使用更新后的扩展名和Content-Type)
|
|
|
+ sysUploadFiles = uploadFileToMinIo(compressedBytes, objectName, fileEtag, compressedExtension, compressedContentType);
|
|
|
} else {
|
|
|
+ // 上传原始文件(非图片或压缩失败时回退)
|
|
|
sysUploadFiles = uploadFileToMinIo(file, objectName, fileEtag, extension);
|
|
|
}
|
|
|
return HttpResult.ok(sysUploadFiles);
|
|
|
@@ -111,6 +145,22 @@ public class FileUploadController extends BaseController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 判断字节数组是否为JPEG格式的图片内容
|
|
|
+ * 通过检测文件魔数(Magic Number)来判断:JPEG的魔数是 FF D8 FF
|
|
|
+ *
|
|
|
+ * @param data 待检测的字节数组
|
|
|
+ * @return 是否为JPEG格式内容
|
|
|
+ */
|
|
|
+ private boolean isJpegContent(byte[] data) {
|
|
|
+ // 参数校验:数据为空或长度不足3字节,无法包含完整的JPEG魔数
|
|
|
+ if (data == null || data.length < 3) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // JPEG格式魔数检测:FF D8 FF
|
|
|
+ return data[0] == (byte) 0xFF && data[1] == (byte) 0xD8 && data[2] == (byte) 0xFF;
|
|
|
+ }
|
|
|
+
|
|
|
@PostMapping("/uploadUrl")
|
|
|
@Operation(summary = "网络文件上传接口", description = "文件上传接口")
|
|
|
public HttpResult<SysUploadFiles> uploadUrlFile(String urlString, String fileName) {
|
|
|
@@ -237,7 +287,6 @@ public class FileUploadController extends BaseController {
|
|
|
.stream(fileInputStream, file.getSize(), -1)
|
|
|
.contentType(file.getContentType())
|
|
|
.build());
|
|
|
-
|
|
|
return insertSysUploadFiles(objectName, extension, objectWriteResponse);
|
|
|
}
|
|
|
|
|
|
@@ -249,7 +298,6 @@ public class FileUploadController extends BaseController {
|
|
|
.stream(fileInputStream, fileBytes.length, -1)
|
|
|
.contentType(contentType)
|
|
|
.build());
|
|
|
-
|
|
|
return insertSysUploadFiles(objectName, extension, objectWriteResponse);
|
|
|
}
|
|
|
|
|
|
@@ -262,7 +310,6 @@ public class FileUploadController extends BaseController {
|
|
|
.stream(fileInputStream, contentLength, -1)
|
|
|
.contentType(contentType)
|
|
|
.build());
|
|
|
-
|
|
|
return insertSysUploadFiles(objectName, extension, objectWriteResponse);
|
|
|
}
|
|
|
|