demo地址:https://github.com/songshijun1995/minio-demo
- 使用docker安装minio
docker run -d -p 9000:9000 -e "MINIO_ACCESS_KEY=admin" -e "MINIO_SECRET_KEY=12345678" -v /storage/data:/data -v /storage/config:/root/.minio --restart=always minio/minio server /data
启动成功后浏览器访问ip:9000
image.png
如果配置nginx代理了ip和端口号可以直接访问域名加/minio
image.png
进入后点击右下角的+,创建一个桶
然后修改桶的权限为读写(点击三个点,然后选择Edit policy)
image.png
image.png
没安装过docker的可以看我以前的文章,详见
- 直接用spring boot官方脚手架创建项目,引入依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.0.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
- 配置yml
server:
port: 8080
servlet:
context-path: /
spring:
application:
name: minio-demo
minio:
endpoint: http://129.28.158.207:9000
accessKey: admin
secretKey: 12345678
filHost: http://file.songshijun.top
logging:
level:
ROOT: info
com.minio: debug
pattern:
file: '%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{50} - %msg%n'
file:
name: ${logging.file.path}/${spring.application.name}.log
path: /home/logs/${spring.application.name}
max-size: 10MB
max-history: 30
4.新建MinioProp类和MinioConfig类,分别加载minio账号密码以及初始MinioClient
package com.minio.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProp {
/**
* 连接地址
*/
private String endpoint;
/**
* 用户名
*/
private String accessKey;
/**
* 密码
*/
private String secretKey;
/**
* 域名
*/
private String filHost;
}
package com.minio.config;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(MinioProp.class)
public class MinioConfig {
@Autowired
private MinioProp minioProp;
/**
* 获取MinioClient
*/
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(minioProp.getEndpoint())
.credentials(minioProp.getAccessKey(), minioProp.getSecretKey())
.build();
}
}
package com.minio.dto.response;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FileUploadResponse {
private String urlHttp;
private String urlPath;
}
5.新建工具类MinioUtil,用来封装上传和删除文件等方法
package com.minio.utils;
import com.minio.config.MinioProp;
import com.minio.dto.response.FileUploadResponse;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Random;
@Slf4j
@Component
public class MinioUtil {
@Autowired
private MinioProp minioProp;
@Autowired
private MinioClient client;
/**
* 创建bucket
*/
public void createBucket(String bucketName) throws Exception {
if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
}
/**
* 上传文件
*/
public FileUploadResponse uploadFile(MultipartFile file, String bucketName) throws Exception {
//判断文件是否为空
if (null == file || 0 == file.getSize()) {
return null;
}
//判断存储桶是否存在 不存在则创建
createBucket(bucketName);
//文件名
String originalFilename = file.getOriginalFilename();
//新的文件名 = 存储桶文件名_时间戳.后缀名
assert originalFilename != null;
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
String fileName = bucketName + "_" +
System.currentTimeMillis() + "_" + format.format(new Date()) + "_" + new Random().nextInt(1000) +
originalFilename.substring(originalFilename.lastIndexOf("."));
//开始上传
client.putObject(
PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(
file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build());
String url = minioProp.getEndpoint() + "/" + bucketName + "/" + fileName;
String urlHost = minioProp.getFilHost() + "/" + bucketName + "/" + fileName;
log.info("上传文件成功url :[{}], urlHost :[{}]", url, urlHost);
return new FileUploadResponse(url, urlHost);
}
/**
* 获取全部bucket
*
* @return
*/
public List<Bucket> getAllBuckets() throws Exception {
return client.listBuckets();
}
/**
* 根据bucketName获取信息
*
* @param bucketName bucket名称
*/
public Optional<Bucket> getBucket(String bucketName) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidResponseException, InternalException, ErrorResponseException, ServerException, XmlParserException {
return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
}
/**
* 根据bucketName删除信息
*
* @param bucketName bucket名称
*/
public void removeBucket(String bucketName) throws Exception {
client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}
/**
* 获取⽂件外链
*
* @param bucketName bucket名称
* @param objectName ⽂件名称
* @param expires 过期时间 <=7
* @return url
*/
public String getObjectURL(String bucketName, String objectName, Integer expires) throws Exception {
return client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).expiry(expires).build());
}
/**
* 获取⽂件
*
* @param bucketName bucket名称
* @param objectName ⽂件名称
* @return ⼆进制流
*/
public InputStream getObject(String bucketName, String objectName) throws Exception {
return client.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
}
/**
* 上传⽂件
*
* @param bucketName bucket名称
* @param objectName ⽂件名称
* @param stream ⽂件流
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
*/
public void putObject(String bucketName, String objectName, InputStream stream) throws
Exception {
client.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, stream.available(), -1).contentType(objectName.substring(objectName.lastIndexOf("."))).build());
}
/**
* 上传⽂件
*
* @param bucketName bucket名称
* @param objectName ⽂件名称
* @param stream ⽂件流
* @param size ⼤⼩
* @param contextType 类型
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
*/
public void putObject(String bucketName, String objectName, InputStream stream, long
size, String contextType) throws Exception {
client.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, size, -1).contentType(contextType).build());
}
/**
* 获取⽂件信息
*
* @param bucketName bucket名称
* @param objectName ⽂件名称
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject
*/
public StatObjectResponse getObjectInfo(String bucketName, String objectName) throws Exception {
return client.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
}
/**
* 删除⽂件
*
* @param bucketName bucket名称
* @param objectName ⽂件名称
* @throws Exception https://docs.minio.io/cn/java-client-apireference.html#removeObject
*/
public void removeObject(String bucketName, String objectName) throws Exception {
client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
}
}
6.新建MinioController
package com.minio.controller;
import com.minio.dto.response.FileUploadResponse;
import com.minio.utils.MinioUtil;
import io.minio.errors.MinioException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Arrays;
@Slf4j
@RestController
@RequestMapping("/file")
public class MinioController {
@Autowired
private MinioUtil minioUtil;
/**
* 上传文件
*/
@PostMapping("/upload")
public FileUploadResponse upload(@RequestParam(name = "file", required = false) MultipartFile file, @RequestParam(required = false) String bucketName) {
FileUploadResponse response = null;
if (StringUtils.isBlank(bucketName)) {
bucketName = "salt";
}
try {
response = minioUtil.uploadFile(file, bucketName);
} catch (Exception e) {
log.error("上传失败 : [{}]", Arrays.asList(e.getStackTrace()));
}
return response;
}
/**
* 删除文件
*/
@DeleteMapping("/delete/{objectName}")
public void delete(@PathVariable("objectName") String objectName, @RequestParam(required = false) String bucketName) throws Exception {
if (StringUtils.isBlank(bucketName)) {
bucketName = "salt";
}
minioUtil.removeObject(bucketName, objectName);
System.out.println("删除成功");
}
/**
* 下载文件到本地
*/
@GetMapping("/download/{objectName}")
public ResponseEntity<byte[]> downloadToLocal(@PathVariable("objectName") String objectName, HttpServletResponse response) throws Exception {
ResponseEntity<byte[]> responseEntity = null;
InputStream stream = null;
ByteArrayOutputStream output = null;
try {
// 获取"myobject"的输入流。
stream = minioUtil.getObject("salt", objectName);
if (stream == null) {
System.out.println("文件不存在");
}
//用于转换byte
output = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int n = 0;
while (-1 != (n = stream.read(buffer))) {
output.write(buffer, 0, n);
}
byte[] bytes = output.toByteArray();
//设置header
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Accept-Ranges", "bytes");
httpHeaders.add("Content-Length", bytes.length + "");
// objectName = new String(objectName.getBytes("UTF-8"), "ISO8859-1");
//把文件名按UTF-8取出并按ISO8859-1编码,保证弹出窗口中的文件名中文不乱码,中文不要太多,最多支持17个中文,因为header有150个字节限制。
httpHeaders.add("Content-disposition", "attachment; filename=" + objectName);
httpHeaders.add("Content-Type", "text/plain;charset=utf-8");
// httpHeaders.add("Content-Type", "image/jpeg");
responseEntity = new ResponseEntity<byte[]>(bytes, httpHeaders, HttpStatus.CREATED);
} catch (MinioException e) {
e.printStackTrace();
} finally {
if (stream != null) {
stream.close();
}
if (output != null) {
output.close();
}
}
return responseEntity;
}
/**
* 在浏览器预览图片
*/
@GetMapping("/preViewPicture/{objectName}")
public void preViewPicture(@PathVariable("objectName") String objectName, HttpServletResponse response) throws Exception {
response.setContentType("image/jpeg");
try (ServletOutputStream out = response.getOutputStream()) {
InputStream stream = minioUtil.getObject("salt", objectName);
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int n = 0;
while (-1 != (n = stream.read(buffer))) {
output.write(buffer, 0, n);
}
byte[] bytes = output.toByteArray();
out.write(bytes);
out.flush();
}
}
}
7.启动项目用postman测试上传文件
image.png
image.png
image.png
8.项目最新版集成swagger2,方便大家测试,可以看看README.md文件
image.png
网友评论