minIO上传完成

main
熊朝柱 3 months ago
parent 0163a2799b
commit 042cbf63dc

@ -81,8 +81,34 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.3.7.RELEASE</version>
</dependency>
<!-- MinIO-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version> <!-- 使用 >=4.8.1 的版本 -->
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>

@ -0,0 +1,25 @@
package cn.com.connor.bh.bhdemo.demos.web.config;
import com.fasterxml.jackson.databind.JsonMappingException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ControllerAdvice;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<String> handleHttpMessageNotReadable(HttpMessageNotReadableException ex) {
Throwable cause = ex.getCause();
if (cause instanceof JsonMappingException) {
JsonMappingException jsonMappingException = (JsonMappingException) cause;
String errorMessage = "Invalid UTF-8 character encountered: " + jsonMappingException.getMessage();
// 记录详细的错误日志
System.err.println(errorMessage);
return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>("Invalid request payload", HttpStatus.BAD_REQUEST);
}
}

@ -0,0 +1,23 @@
package cn.com.connor.bh.bhdemo.demos.web.config;
import cn.com.connor.bh.bhdemo.demos.web.entity.MinioPojo;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration()
public class MinIOConfig {
@Autowired
private MinioPojo minioPojo;
@Bean
public MinioClient minioClient(){
return MinioClient.builder()
.endpoint(minioPojo.getUrl()) //传入url地址
//传入用户名和密码
.credentials(minioPojo.getAccessKey(), minioPojo.getSecretKey())
.build(); //完成MinioClient的初始化
}
}

@ -0,0 +1,37 @@
//package cn.com.connor.bh.bhdemo.demos.web.config;
//
//import com.fasterxml.jackson.databind.ObjectMapper;
//import org.apache.commons.io.IOUtils;
//import org.springframework.http.HttpInputMessage;
//import org.springframework.http.converter.HttpMessageNotReadableException;
//import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
//
//import java.io.IOException;
//import java.nio.charset.StandardCharsets;
//
//public class Utf8SafeMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
// private final ObjectMapper objectMapper;
//
// public Utf8SafeMappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
// this.objectMapper = objectMapper;
// }
//
// @Override
// protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
// byte[] body = IOUtils.toByteArray(inputMessage.getBody());
// try {
// // 检查是否为有效UTF-8
// String content = new String(body, StandardCharsets.UTF_8);
// validateUtf8(content);
// return objectMapper.readValue(content, clazz);
// } catch (IllegalArgumentException e) {
// throw new HttpMessageNotReadableException("Invalid UTF-8 encoding in request body", e, inputMessage);
// }
// }
//
// private void validateUtf8(String content) {
// if (!StandardCharsets.UTF_8.newEncoder().canEncode(content)) {
// throw new IllegalArgumentException("Invalid UTF-8 characters found");
// }
// }
//}

@ -0,0 +1,22 @@
//package cn.com.connor.bh.bhdemo.demos.web.config;
//
//import com.fasterxml.jackson.databind.ObjectMapper;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.http.converter.HttpMessageConverter;
//import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//
//import java.util.List;
//
//@Configuration
//public class WebConfig implements WebMvcConfigurer {
// @Bean
// public Utf8SafeMappingJackson2HttpMessageConverter utf8SafeMappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
// return new Utf8SafeMappingJackson2HttpMessageConverter(objectMapper);
// }
//
// @Override
// public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// converters.add(utf8SafeMappingJackson2HttpMessageConverter(new ObjectMapper()));
// }
//}

@ -0,0 +1,131 @@
package cn.com.connor.bh.bhdemo.demos.web.controller;
import cn.com.connor.bh.bhdemo.demos.web.entity.MinIOUploadRequestPojo;
import cn.com.connor.bh.bhdemo.demos.web.service.MinIOService;
import cn.hutool.core.codec.Base64;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/minio")
public class MinIOController {
private static final Logger logger = LoggerFactory.getLogger(MinIOController.class);
private final ObjectMapper objectMapper;
public MinIOController(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Autowired
private MinIOService minIOService;
@PostMapping("/upload")
public ResponseEntity<Map<String, String>> uploadFile(@RequestParam("filePath") MultipartFile file,
@RequestParam("bucketFileName") String bucketFileName) throws Exception {
Map<String, String> response = new HashMap<>();
try {
logger.info("开始上传文件到MinIO上传目标位置{}", bucketFileName);
logger.info("开始上传文件到MinIO上传目标位置{},原始文件:{}", bucketFileName, file.getOriginalFilename());
if (bucketFileName == null || bucketFileName.isEmpty()) {
logger.error("上传文件路径或文件名不能为空!");
response.put("message", "上传文件路径或文件名不能为空!");
return ResponseEntity.badRequest().body(response);
}
String resultUrl = minIOService.uploadFile(file, bucketFileName);
if (resultUrl != null){
response.put("message", "File uploaded successfully");
response.put("url", resultUrl);
}else {
response.put("message", "文件上传失败,请联系管理员。");
}
return ResponseEntity.ok(response);
} catch (IllegalArgumentException e) {
logger.error("文件上传参数非法", e);
response.put("message", "非法参数:" + e.getMessage());
return ResponseEntity.badRequest().body(response);
} catch (Exception e) {
logger.error("文件上传过程中发生未知异常", e);
response.put("message", "文件上传失败,请联系管理员。");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
}
/**
* minIO
* @param directory
* @return
* @throws Exception
*/
@PostMapping("/deleteByDir")
public ResponseEntity<Map<String, String>> deleteByDir(@RequestParam("directory") String directory) throws Exception {
Map<String, String> response = new HashMap<>();
try {
logger.info("开始清空MinIO目录清空目标位置{}", directory);
if (directory == null || directory.isEmpty()) {
logger.error("清空minIO目录路径不能为空");
response.put("message", "清空minIO目录路径不能为空");
return ResponseEntity.badRequest().body(response);
}
boolean deletResult = minIOService.deleteByDir(directory);
if (deletResult){
response.put("message", "清空minIO目录成功");
return ResponseEntity.ok(response);
}else {
response.put("message", "清空minIO目录失败请联系管理员。");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
}catch (Exception e) {
logger.error("清空minIO目录过程中发生未知异常", e);
response.put("message", "清空minIO目录失败请联系管理员。");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
}
private String parseValueFromBody(String body, String paramName) {
String[] parts = body.split("&");
for (String part : parts) {
String[] keyValue = part.split("=");
if (keyValue.length == 2 && keyValue[0].equals(paramName)) {
try {
return URLDecoder.decode(keyValue[1], "GBK");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
return null;
}
public static String removeFirstAtSymbol(String str,String symbol) {
int atIndex = str.indexOf(symbol);
if (atIndex != -1) {
return str.substring(0, atIndex) + str.substring(atIndex + 1);
}
return str; // 如果没有找到 @ 符号,返回原字符串
}
private String cleanString(String input) {
// 使用正则表达式或其他方法清理无效字符
return input.replaceAll("[^\\x00-\\x7F]", "");
}
}

@ -0,0 +1,15 @@
package cn.com.connor.bh.bhdemo.demos.web.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class MinIOUploadRequestPojo {
private String filePath; // 文件在客户端或服务器上的路径
private String bucketFileName; // 在MinIO中的目标文件名
}

@ -0,0 +1,20 @@
package cn.com.connor.bh.bhdemo.demos.web.entity;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@ConfigurationProperties(prefix = "minio")
@Component
@Data
public class MinioPojo {
private String url;
private String username;
private String password;
//桶名
private String bucketName;
private String accessKey;
private String secretKey;
}

@ -0,0 +1,139 @@
package cn.com.connor.bh.bhdemo.demos.web.service;
import cn.com.connor.bh.bhdemo.demos.web.entity.MinioPojo;
import io.minio.*;
import io.minio.messages.Item;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Component
public class MinIOService {
private static final Logger logger = LoggerFactory.getLogger(MinIOService.class);
@Autowired
private MinioClient minioClient;
@Autowired
private MinioPojo minioPojo;
/**
*
* @param file
* @param bucketfilename minIO2021/01/01/.PDF
* @return
*/
public String uploadFile(MultipartFile file, String bucketfilename) {
logger.info("service-uploadFile:开始上传文件到MinIO");
String returnInfo = null;
// 检查文件是否为空
if (file.getSize() == 0 || file.isEmpty()) {
throw new IllegalArgumentException("上传的文件无效或为空!");
}
String bucketName = minioPojo.getBucketName();
try {
logger.info("service-uploadFile:开始上传文件到MinIO=> " + minioPojo.getUrl() + "/" + bucketName + "/" + bucketfilename);
logger.info("01");
// 判断桶是否存在
boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!bucketExists) {
logger.info("服务-uploadFile:桶不存在,正在创建桶 => " + bucketName);
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
logger.info("02");
// 使用 MultipartFile 的输入流上传文件
try (InputStream inputStream = file.getInputStream()) {
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(bucketfilename)
.stream(inputStream, file.getSize(), -1)
.contentType(file.getContentType())
.build());
}
logger.info("03");
// 构造返回的文件访问 URL
URI fileUri = new URI(minioPojo.getUrl() + "/" + bucketName + bucketfilename);
returnInfo = fileUri.toString();
}catch (IllegalArgumentException e1){
logger.error("服务-uploadFile:参数错误", e1);
throw new IllegalArgumentException("文件上传失败!", e1);
}catch (Exception e) {
logger.error("服务-uploadFile:未知错误", e);
throw new RuntimeException("文件上传失败!", e);
}
return returnInfo;
}
/**
*
* @param filePath
* @return truefalse
*/
private boolean isFileExists(String filePath) {
File file = new File(filePath);
return file.exists() && file.isFile();
}
/**
*
* @param file
* @return
*/
private String getContentType(File file) {
String fileName = file.getName();
if (fileName.endsWith(".pdf")) {
return "application/pdf";
} else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
return "image/jpeg";
} else if (fileName.endsWith(".png")) {
return "image/png";
} else if (fileName.endsWith(".txt")) {
return "text/plain";
} else {
return "application/octet-stream";
}
}
/**
* minIO
* @param directory "/2023/01/01/"
* @return truefalse
*/
public boolean deleteByDir(String directory) {
logger.info("service-deleteByDir:开始删除目录下的所有文件 => " + directory);
boolean result = false;
String bucketName = minioPojo.getBucketName();
try {
// 列出目录下的所有对象
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).prefix(directory).build());
for (Result<Item> resultItem : results) {
Item item = resultItem.get();
// 删除每个对象
minioClient.removeObject(
RemoveObjectArgs.builder().bucket(bucketName).object(item.objectName()).build());
}
result = true;
} catch (Exception e) {
logger.error("服务-deleteByDir: 删除文件失败", e);
}
return result;
}
}

@ -9,14 +9,23 @@ spring:
rabbitmq:
host: 192.168.3.125
port: 5672
username: kuma
password: 123
username: guest
password: guest
template:
default-receive-queue: StoCAPP
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: cn.com.connor.bh.bhdemo.demos.web.entity
configuration:
map-underscore-to-camel-case: true
#MinIO上传配置
minio:
url: http://192.168.3.125:9000
username: minioadmin
password: minioadmin
bucketName: bhdemo

@ -7,16 +7,27 @@ spring:
username: infodba
password: infodba
rabbitmq:
host: 192.168.3.125
host: localhost
port: 5672
username: kuma
password: 123
virtual-host: whbh
username: admin
password: admin
template:
default-receive-queue: StoCAPP
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: cn.com.connor.bh.bhdemo.demos.web.entity
configuration:
map-underscore-to-camel-case: true
#MinIO上传配置
minio:
url: http://127.0.0.1:9005
username: minioadmin
password: PSlCwr3gQaJl6aFHT4jA
bucketName: tcfile
accessKey: PSlCwr3gQaJl6aFHT4jA #访问的key
secretKey: 5fwp5Umfo2I8kr76CINbNjuGDBHvRxHQEhPjeRRl #访问的密钥

@ -33,3 +33,7 @@ spring:
active: test #需要使用的配置文件的后缀
server:
port: 8080
servlet:
multipart:
max-file-size=100MB:
max-request-size=100MB:

Loading…
Cancel
Save