Minio上传图片
上传图片
1. 准备环境
(1) 在nacos中配置
minio:
endpoint: http://192.168.65.129:9001
accessKey: minioadmin190715
secretKey: minioadmin190715
bucket:
files: mediafiles
videofiles: video
(2) minio配置类
/**
* @Author: ciky
* @Description: minio配置类
* @DateTime: 2024/6/2 14:30
**/
@Configuration
public class MinioConfig {
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey}")
private String secretKey;
@Bean
public MinioClient minioClient(){
MinioClient minioClient =
MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
return minioClient;
}
}
2. 接口设计分析-JSON
请求地址:/media/upload/coursefile
请求内容:Content-Type: multipart/form-data; //---->对应的类型是(MediaType.MULTIPART_FORM_DATA_VALUE)
form-data; name="filedata"; filename="具体的文件名称"
响应参数:文件信息,如下
JSON
{
"id": "a16da7a132559daf9e1193166b3e7f52",
"companyId": 1232141425,
"companyName": null,
"filename": "1.jpg",
"fileType": "001001",
"tags": "",
"bucket": "/testbucket/2022/09/12/a16da7a132559daf9e1193166b3e7f52.jpg",
"fileId": "a16da7a132559daf9e1193166b3e7f52",
"url": "/testbucket/2022/09/12/a16da7a132559daf9e1193166b3e7f52.jpg",
"timelength": null,
"username": null,
"createDate": "2022-09-12T21:57:18",
"changeDate": null,
"status": "1",
"remark": "",
"auditStatus": null,
"auditMind": null,
"fileSize": 248329
}
3. 响应模型类-DTO
@Data
public class UploadFileResultDto extends MediaFiles {
//后续可能需要添加其他内容
//继承MediaFiles
//方便以后修改
//MediaFiles是数据库表的po类,不能随便修改
}
/**
* @Author: ciky
* @Description: 文件信息
* @DateTime: 2024/6/2 17:50
**/
@Data
public class UploadFileParamsDto {
/**
* 文件名称
*/
private String filename;
/**
* 文件类型(文档,音频,视频)
*/
private String fileType;
/**
* 文件大小
*/
private Long fileSize;
/**
* 标签
*/
private String tags;
/**
* 上传人
*/
private String username;
/**
* 备注
*/
private String remark;
}
4. 定义接口层-Controller
5. 持久层-Mapper
- 由MybatisPlus提供
6. 业务层-Service
(1) 上传文件到minio
@Autowired
private MediaFileService currentProxy;
@Override
public UploadFileResultDto uploadFile(Long companyId, UploadFileParamsDto uploadFileParamsDto, String localFilePath) {
//1.将文件上传到minio
//获取文件名
String filename = uploadFileParamsDto.getFilename();
//得到拓展名
String extension = filename.substring(filename.lastIndexOf("."));
//得到mimeType
String mimeType = getMimeType(extension); //------------------------------调用抽取的方法1
String defaultFolderPath = getDefaultFolderPath(); //------------------------------调用抽取的方法2
//文件的md5值
String fileMd5 = getFileMd5(new File(localFilePath)); //------------------------------调用抽取的方法3
//得到objectname
String objectName = defaultFolderPath + fileMd5 + extension;
addMediaFilesToMinIO(bucket_mediafiles, localFilePath, mimeType, objectName); //------------------------------调用抽取的方法4
//2.将文件信息保存到数据库-----------------------------------------------调用抽取的方法5
MediaFiles mediaFiles = currentProxy.addMediaFilesToDb(companyId, fileMd5, uploadFileParamsDto, bucket_mediafiles, objectName);
if(mediaFiles==null){
XueChengPlusException.cast("文件上传后保存信息失败");
}
//准备返回的对象
UploadFileResultDto uploadFileResultDto = new UploadFileResultDto();
BeanUtils.copyProperties(mediaFiles,uploadFileResultDto);
return uploadFileResultDto;
}
/**
* 根据扩展名获取mimeType //------------------------------抽取方法1
*/
private String getMimeType(String extension) {
if (extension == null) {
extension = "";
}
//根据扩展名获取mimeType
ContentInfo extensionMatch = ContentInfoUtil.findExtensionMatch(extension);
String mimeType = MediaType.APPLICATION_STREAM_JSON_VALUE; //通用mimeType(字节流)
if (extensionMatch != null) {
mimeType = extensionMatch.getMimeType();
}
return mimeType;
}
/**
* 获取文件默认存储目录路径(年/月/日) //------------------------------抽取方法2
*/
private String getDefaultFolderPath() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String folder = sdf.format(new Date()).replace("-", "/") + "/";
return folder;
}
/**
* 获取文件的md5值 //------------------------------抽取方法3
*/
private String getFileMd5(File file) {
try {
String fileMd5 = DigestUtils.md5Hex(new FileInputStream(file));
return fileMd5;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 上传文件到minio //------------------------------抽取方法4
*
* @param bucket 桶
* @param localFilePath 本地文件路径
* @param mimeType 媒资类型
* @param objectName 对象名
* @return
*/
public boolean addMediaFilesToMinIO(String bucket, String localFilePath, String mimeType, String objectName) {
try {
minioClient.uploadObject(UploadObjectArgs.builder()
.bucket(bucket) //minio中的桶
.filename(localFilePath) //本地文件路径
.object(objectName) //对象名,minio桶中的路径
.contentType(mimeType) //媒体文件类型
.build());
return true;
} catch (Exception e) {
e.printStackTrace();
log.error("上传文件出错,bucket{},objectName{},错误信息{}", bucket, objectName, e.getMessage());
}
return false;
}
(2) 保存文件到数据库
/**
* 将文件信息添加到文件表 //------------------------------抽取方法5
* @param companyId 机构id
* @param fileMd5 文件md5值
* @param uploadFileParamsDto 上传文件的信息
* @param bucket 桶
* @param objectName 对象名称
* @return com.xuecheng.media.model.po.MediaFiles
*/
@Transactional
public MediaFiles addMediaFilesToDb(Long companyId,String fileMd5,UploadFileParamsDto uploadFileParamsDto,String bucket,String objectName) {
MediaFiles mediaFiles = mediaFilesMapper.selectById(fileMd5);
if(mediaFiles==null){
mediaFiles = new MediaFiles();
BeanUtils.copyProperties(uploadFileParamsDto,mediaFiles);
//文件id
mediaFiles.setId(fileMd5);
//机构id
mediaFiles.setCompanyId(companyId);
//桶
mediaFiles.setBucket(bucket);
//file_path
mediaFiles.setFilePath(objectName);
//file_id
mediaFiles.setFileId(fileMd5);
//url
mediaFiles.setUrl("/"+bucket+"/"+objectName);
//上传时间
mediaFiles.setCreateDate(LocalDateTime.now());
//状态
mediaFiles.setStatus("1");
//审核状态
mediaFiles.setAuditStatus("002003");
//插入数据库
int insert = mediaFilesMapper.insert(mediaFiles);
if(insert<=0){
log.debug("向数据库保存文件失败,bucket{},objectName{}",bucket,objectName);
return null;
}
}
return mediaFiles;
}
7. 完善接口层-Controller
@ApiOperation("上传图片")
@RequestMapping(value = "/upload/coursefile",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public UploadFileResultDto upload(@RequestPart("filedata")MultipartFile filedata) throws IOException {
//准备上传文件的信息
UploadFileParamsDto uploadFileParamsDto = new UploadFileParamsDto();
//原始文件名称
uploadFileParamsDto.setFilename(filedata.getOriginalFilename());
//文件大小
uploadFileParamsDto.setFileSize(filedata.getSize());
//文件类型
uploadFileParamsDto.setFileType("001001");
//创建一个临时文件
File tempFile = File.createTempFile("minio", ".temp");
filedata.transferTo(tempFile);
//文件路径
String localFilePath = tempFile.getAbsolutePath();
//机构id
Long companyId = 1232141425L;
//调用service上传图片
mediaFileService.uploadFile(companyId,uploadFileParamsDto,localFilePath);
return null;
}
8. httpClient测试
### 上传文件
POST {{media_host}}/media/upload/coursefile
Content-Type: multipart/form-data; boundary=WebAppBoundary
--WebAppBoundary
Content-Disposition: form-data; name="filedata"; filename="1.png"
Content-Type: application/octet-stream
< D:\c盘移动\1.png
9. Service事务优化
//修改前
@Override
@Transactional
public UploadFileResultDto uploadFile(Long companyId, UploadFileParamsDto uploadFileParamsDto, String localFilePath) {
//1.将文件上传到minio
......
//2.将文件信息保存到数据库
MediaFiles mediaFiles = addMediaFilesToDb(companyId, fileMd5, uploadFileParamsDto, bucket_mediafiles, objectName);
......
}
public MediaFiles addMediaFilesToDb(Long companyId,String fileMd5,UploadFileParamsDto uploadFileParamsDto,String bucket,String objectName) {
......
}
public interface MediaFileService {
......
}
-
问题:uploadFile方法上添加@Transactional注解,但方法中有网络请求的内容(除了数据库之外的网络请求)
网络请求时间可能很长,数据库链接释放就会变慢,数据库链接可能不够用
-
解决:将uploadFile方法上的@Transactional注解去掉,加在其中的addMediaFilesToDb方法上
-
新问题:addMediaFilesToDb方法无法被事务控制
-
分析:addMediaFilesToDb方法不是通过代理对象去调用的
-
解决:
将自己注入
将addMediaFilesToDb方法提成接口
通过代理对象调用
-
总结:要想进行事务控制,需同时满足两个条件
(1) 添加@Transactional注解
(2) 通过代理对象调用方法
//修改后
@Autowired
private MediaFileService currentProxy;
@Override
public UploadFileResultDto uploadFile(Long companyId, UploadFileParamsDto uploadFileParamsDto, String localFilePath) {
//1.将文件上传到minio
......
//2.将文件信息保存到数据库
MediaFiles mediaFiles = currentProxy.addMediaFilesToDb(companyId, fileMd5, uploadFileParamsDto, bucket_mediafiles, objectName);
......
}
@Transactional
public MediaFiles addMediaFilesToDb(Long companyId,String fileMd5,UploadFileParamsDto uploadFileParamsDto,String bucket,String objectName) {
......
}
public interface MediaFileService {
public MediaFiles addMediaFilesToDb(Long companyId,String fileMd5,UploadFileParamsDto uploadFileParamsDto,String bucket,String objectName);
......
}