minio部署及接口封装升级优化
一、单机部署步骤
与集群部署不同点:
- 启动脚本
nohup ./minio server /data/minio/ &
- 单机服务器不同于集群部署,需要最4个存储,单存储就可以
二、多级目录创建
- minio桶(bucketName)是一个全局唯一的,只作为一级节点,不能为多级节点
- 数据对象(objectName)是桶里的存储对象,多级目录可以从改参数入手
- 参考 springboot 集成 Minio 文件服务器博文 修改上传接口,重点在于拆分bucket参数构建objectName。
eg:
入参buckt :data/template/plan file:xxxx.jpg
构建: bucket:data objectName:/template/plan/xxxx.jpg
private final String splitChar = "/";
@Value("${minio-bucketName}")
private String bucketName;
@Override
public AttachmentOutDTO upload(MultipartFile file, String bucket) {
final String fileName = file.getOriginalFilename();
AttachmentOutDTO dto = new AttachmentOutDTO();
//一些校验
BizPreconditions.checkArgumentNoStack(file.getSize() < MAX_UPLOAD_SIZE,
"超出附件大小限制" + (MAX_UPLOAD_SIZE / 1024) / 1024 + "M");
BizPreconditions.checkArgumentNoStack(!file.isEmpty(), "上传附件不能为空!");
BizPreconditions.checkArgumentNoStack(fileName.length() < MinioConstants.FILENAMELENGTH,
"附件名称长度不能超过100!,此附件名长度为: " + fileName.length());
//不支持向模板目录上传文件
BizPreconditions.checkArgumentNoStack(!MinioConstants.TEMPLATE.equals(bucket), MinioConstants.TEMPLATE + "为保留桶,请更换桶名称!");
// 得到文件流
final InputStream is;
try {
is = file.getInputStream();
String attachId = UUIDUtil.uuid();
String fileHz = fileName.substring(fileName.lastIndexOf("."));
String newFileName = attachId + fileHz;
//前端传递多级bucket时对bucket拆分
if (bucket != null && bucketName.contains(splitChar)) {
newFileName = bucket.substring(bucket.indexOf(splitChar) + 1) + splitChar + newFileName;
bucket = bucket.split(splitChar)[0];
}
//前端不传递bucket时取配置中的bucketName
else if (bucketName.contains(splitChar)) {
newFileName = bucketName.substring(bucketName.indexOf(splitChar) + 1) + splitChar + newFileName;
bucket = bucketName.split(splitChar)[0];
}
bucket = minioTemplate.putObject(bucket, newFileName, file);
String objectUrl = minioTemplate.getObjectUrl(bucket, newFileName);
// 关闭输入流
is.close();
dto.setName(fileName);
dto.setUrl(objectUrl);
dto.setFileId(attachId);
//中间路径
String midDirPath = bucket + File.separator;
//path是一个不完整路径,缺少路径根目录
String midDirPathAndFileName = midDirPath + newFileName;
//附件信息入库
attachmentService.uploadAttachment(attachId, midDirPathAndFileName, "userId", fileName);
return dto;
} catch (Exception e) {
e.printStackTrace();
throw new BusinessException("文件上传失败");
}
}
三、url图片数据预览
minio java封装后保存了附件信息,主要包括id,path、oldname,path是minio保存路径,添加minio外网代理地址后可获取到上传图片访问地址,但minio默认并不支持直接预览,它会将xxxx.jpg视为一个目录而不是图片。
客户端可以设置所在桶Policy为只读即可。
代码端修改MinioTemplate:调用创建桶时会自动将该桶设置为只读。
/**
* 创建bucket
* setBucketPolicy 设置权限才可以预览
* @param bucketName bucket名称
*/
@SneakyThrows
public void createBucket(String bucketName) {
if (!bucketExists(bucketName)) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
StringBuilder builder = new StringBuilder();
builder.append("{\n");
builder.append(" \"Statement\": [\n");
builder.append(" {\n");
builder.append(" \"Action\": [\n");
builder.append(" \"s3:GetBucketLocation\",\n");
builder.append(" \"s3:ListBucket\"\n");
builder.append(" ],\n");
builder.append(" \"Effect\": \"Allow\",\n");
builder.append(" \"Principal\": \"*\",\n");
builder.append(" \"Resource\": \"arn:aws:s3:::my-bucketname\"\n");
builder.append(" },\n");
builder.append(" {\n");
builder.append(" \"Action\": \"s3:GetObject\",\n");
builder.append(" \"Effect\": \"Allow\",\n");
builder.append(" \"Principal\": \"*\",\n");
builder.append(" \"Resource\": \"arn:aws:s3:::my-bucketname/*.*\"\n");
builder.append(" }\n");
builder.append(" ],\n");
builder.append(" \"Version\": \"2012-10-17\"\n");
builder.append("}\n");
minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(builder.toString().replace("my-bucketname",bucketName)).build());
}
}
四、遇到的问题
ErrorResponse(code = AccessDenied, message = Access denied, bucketName = data…
minio服务器和当前应用服务器分离部署时,需要保证两个服务器时间相差不大