第一步:拉取镜像
docker pull minio/minio
第二步:运行镜像
docker run \
-p 19000:9000 \
-p 19001:9001 \
--name sifan-minio \
-d --restart=always \
-v /root/sifan/minio/data:/data \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=123456" \
minio/minio:latest server /data --console-address ":9001"
第三步、开放阿里云端口19000好19001
访问控制台:http://118.31.8.23:19001/login
连接失败
看一下容器运行状况
发现minio端口没有映射,可以是配置出问题了
看一下容器日志
docker logs sifan-minio
发现一直报这个错误,并且容器一直会重启,启动是cpu彪增
这个错误意思是秘密最少要8个字符,而123456之后6个字符,明显不符合要求
停掉容器
docker stop sifan-minio
删除容器
docker rm -f sifan-minio
修改秘密长度重新启动镜像
docker run \
-p 19000:9000 \
-p 19001:9001 \
--name sifan-minio \
-d --restart=always \
-v /root/sifan/minio/data:/data \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=12345678" \
minio/minio:latest server /data --console-address ":9001"
这下看一下容器启动情况
看下日志
完美,没有报错,使用浏览器访问下看看
http://118.31.8.23:19001/login
完美,可以访问了
注意:日志里面的Console: http://172.17.0.2:9001 http://127.0.0.1:9001,是容器的端口,而启动是映射了linux操作系统的19001端口,浏览器访问19001端口时linux会通过网桥访问容器的9001端口。
使用Springboot整合minio
引入依赖,需要引入okhttp的原因是minio需要依赖okhttp4
<!--minio-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.1</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.8.1</version>
<scope>compile</scope>
</dependency>
配置文件
minio:
# minio配置的地址,端口19000
url: http://118.31.8.23:19000
# 账号
accessKey: admin
# 密码
secretKey: 12345678
# MinIO桶名字
bucketName: sifanOS
配置类
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
/**
* 服务地址
*/
public String url;
/**
* 用户名
*/
public String accessKey;
/**
* 密码
*/
public String secretKey;
/**
* 存储桶名称
*/
public String bucketName;
// "如果是true,则用的是https而不是http,默认值是true"
public static Boolean secure = false;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getAccessKey() {
return accessKey;
}
public void setAccessKey(String accessKey) {
this.accessKey = accessKey;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
public String getBucketName() {
return bucketName;
}
public void setBucketName(String bucketName) {
this.bucketName = bucketName;
}
public static Boolean getSecure() {
return secure;
}
public static void setSecure(Boolean secure) {
MinioConfig.secure = secure;
}
@Bean
public MinioClient getMinioClient() {
return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();
}
}
这是idea可以会提示缺少
configuration-processor
按照idea的提示引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
打包工具排除configuration-processor
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
idea可能还提示要你下载big data插件,按提示下载重启即可
工具类
import com.sifan.erp.domain.ObjectItem;
import io.minio.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class MinioUtil {
@Resource
private MinioClient minioClient;
/**
* 查看存储bucket是否存在
*
* @param bucketName 存储bucket
* @return boolean
*/
public Boolean bucketExists(String bucketName) {
Boolean found;
try {
found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return found;
}
/**
* 创建存储bucket
*
* @param bucketName 存储bucket名称
* @return Boolean
*/
public Boolean makeBucket(String bucketName) {
try {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 删除存储bucket
*
* @param bucketName 存储bucket名称
* @return Boolean
*/
public Boolean removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 文件上传
*
* @param file 文件
* @param bucketName 存储bucket
* @return Boolean
*/
public Boolean upload(MultipartFile file, String fileName, String bucketName) {
try {
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(fileName)
.stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
//文件名称相同会覆盖
minioClient.putObject(objectArgs);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 文件下载
*
* @param bucketName 存储bucket名称
* @param fileName 文件名称
* @param res response
* @return Boolean
*/
public void download(String bucketName, String fileName, HttpServletResponse res) {
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName)
.object(fileName).build();
try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
byte[] buf = new byte[1024];
int len;
try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
while ((len = response.read(buf)) != -1) {
os.write(buf, 0, len);
}
os.flush();
byte[] bytes = os.toByteArray();
res.setCharacterEncoding("utf-8");
//设置强制下载不打开
res.setContentType("application/force-download");
res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
try (ServletOutputStream stream = res.getOutputStream()) {
stream.write(bytes);
stream.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 查看文件对象
*
* @param bucketName 存储bucket名称
* @return 存储bucket内文件对象信息
*/
public List<ObjectItem> listObjects(String bucketName) {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).build());
List<ObjectItem> objectItems = new ArrayList<>();
try {
for (Result<Item> result : results) {
Item item = result.get();
ObjectItem objectItem = new ObjectItem();
objectItem.setObjectName(item.objectName());
objectItem.setSize(item.size());
objectItems.add(objectItem);
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return objectItems;
}
/**
* 批量删除文件对象
*
* @param bucketName 存储bucket名称
* @param objects 对象名称集合
*/
public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());
Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
return results;
}
}
实体类
@Data
public class ObjectItem {
private String objectName;
private Long size;
}
service层
import org.springframework.web.multipart.MultipartFile;
public interface SysFileService {
/**
* 文件上传*
*
* @param file 上传的文件
* @return 返回的url
*/
String uploadFileMinio(MultipartFile file);
}
serviceImpl
@Service
public class SysFileServiceInpl implements SysFileService {
@Resource
private MinioConfig minioConfig;
@Resource
private MinioUtil minioUtil;
@Override
public String uploadFileMinio(MultipartFile file) {
boolean flag = false;
if (file.isEmpty()) {
throw new RuntimeException("文件不存在!");
}
// 判断存储桶是否存在
if (!minioUtil.bucketExists(minioConfig.getBucketName())) {
minioUtil.makeBucket(minioConfig.getBucketName());
}
// 生成文件名
String fineName = FileUploadUtils.extractFilename(file);
try {
// 上传文件
flag = minioUtil.upload(file, fineName, minioConfig.getBucketName());
} catch (Exception e) {
return null;
}
// 判断是否上传成功,成功就返回url,不成功就返回null
if (flag) {
return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fineName;
}
return null;
}
}
FileUploadUtils工具类
import java.util.Date;
import java.util.UUID;
/**
* 文件上传工具类
*/
public class FileUploadUtils {
/**
* 编码文件名 如 : images/user/2020/12/4/***.png
*/
public static final String extractFilename(MultipartFile file) {
String fileName = file.getOriginalFilename();
String extension = getExtension(file);
fileName = DateFormatUtils.format(new Date(), "yyyy/MM/dd") + "/" + UUID.randomUUID().toString().replaceAll("-", "") + "." + extension;
return fileName;
}
/**
* 获取文件名的后缀
*
* @param file 表单文件
* @return 后缀名
*/
public static final String getExtension(MultipartFile file) {
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
if (!StrUtil.isNotEmpty(extension)) {
extension = MimeTypeUtils.getExtension(file.getContentType());
}
return extension;
}
}
媒体类型工具类
/**
* 媒体类型工具类
*/
public class MimeTypeUtils {
public static final String IMAGE_PNG = "image/png";
public static final String IMAGE_JPG = "image/jpg";
public static final String IMAGE_JPEG = "image/jpeg";
public static final String IMAGE_BMP = "image/bmp";
public static final String IMAGE_GIF = "image/gif";
public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
public static final String[] DEFAULT_ALLOWED_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
public static String getExtension(String prefix) {
switch (prefix) {
case IMAGE_PNG:
return "png";
case IMAGE_JPG:
return "jpg";
case IMAGE_JPEG:
return "jpeg";
case IMAGE_BMP:
return "bmp";
case IMAGE_GIF:
return "gif";
default:
return "";
}
}
}
控制器
@RestController
@RequestMapping("/system/file")
public class SysFileController {
@Resource
private SysFileService sysFileService;
@PostMapping("/upload")
public Result uploadFileMinio(MultipartFile file) {
String url = sysFileService.uploadFileMinio(file);
Result result = new Result();
if (StrUtil.isNotEmpty(url)) {
result.setCode(200);
result.setMessage("上传成功");
result.setData(url);
} else {
result.setMessage("上传失败");
}
return result;
}
}