demo地址:https://github.com/songshijun1995/minio-demo
- 使用docker安裝minio
docker run -d -p 9000:9000 --name minio\
-e "MINIO_ACCESS_KEY=admin" \
-e "MINIO_SECRET_KEY=admin123" \
-v /usr/local/minio/data:/data \
-v /usr/local/minio/config:/root/.minio \
minio/minio:RELEASE.2021-06-17T00-10-46Z server /data
啟動(dòng)成功后瀏覽器訪問ip:9000

image.png
如果配置nginx代理了ip和端口號(hào)可以直接訪問域名加/minio

image.png
進(jìn)入后點(diǎn)擊右下角的+,創(chuàng)建一個(gè)桶

image.png
然后修改桶的權(quán)限為讀寫(點(diǎn)擊三個(gè)點(diǎn),然后選擇Edit policy)

image.png

image.png
沒安裝過docker的可以看我以前的文章,詳見
- 直接用spring boot官方腳手架創(chuàng)建項(xiàng)目,引入依賴
<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賬號(hào)密碼以及初始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;
/**
* 創(chuàng)建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;
}
//判斷存儲(chǔ)桶是否存在 不存在則創(chuàng)建
createBucket(bucketName);
//文件名
String originalFilename = file.getOriginalFilename();
//新的文件名 = 存儲(chǔ)桶文件名_時(shí)間戳.后綴名
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();
}
/**
* 根據(jù)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();
}
/**
* 根據(jù)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 過期時(shí)間 <=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 ?進(jìn)制流
*/
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("文件不存在");
}
//用于轉(zhuǎn)換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();
//設(shè)置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個(gè)中文,因?yàn)閔eader有150個(gè)字節(jié)限制。
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;
}
/**
* 在瀏覽器預(yù)覽圖片
*/
@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.啟動(dòng)項(xiàng)目用postman測試上傳文件

image.png

image.png

image.png
8.項(xiàng)目最新版集成swagger2,方便大家測試,可以看看README.md文件

image.png