feat(storage): 添加MinIO对象存储支持
实现基于MinIO的对象存储功能,包括: 1. 新增MinioProperties配置类 2. 创建UploadStorage接口及Minio/Local实现 3. 重构MediaLibraryService使用统一存储接口 4. 添加MinIO模式下文件服务控制器 5. 提供docker-compose.minio.yml部署配置 6. 更新文档说明MinIO集成方式 支持本地存储和MinIO对象存储两种模式,可通过upload.storage配置切换
This commit is contained in:
@@ -5,6 +5,10 @@
|
||||
- **用于生产一次性导入**:`wuhan_saga_prod_YYYYMMDD.sql`(全库 `mysqldump`,含结构与数据)。
|
||||
- 导入后**不要**再按顺序重放本目录下历史 `patch_*.sql` / `migration_*.sql`(除非你在维护很老的库且确认缺列)。
|
||||
|
||||
## 附件与 MinIO
|
||||
|
||||
- 图片路径在库中仍为 `/uploads/{分类}/{文件名}`。`upload.storage=minio` 时文件在对象存储桶内键为 `uploads/...`,需把原 `uploads/` 目录同步进桶(见仓库 `docker-compose.minio.yml` 与后端 `application.yml` 中 `minio.*`)。
|
||||
|
||||
## 日常开发以代码为准的 DDL
|
||||
|
||||
| 文件 | 用途 |
|
||||
|
||||
19
docker-compose.minio.yml
Normal file
19
docker-compose.minio.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
# 本地/内网 MinIO(与 Spring upload.storage=minio 配套)
|
||||
# 启动:docker compose -f docker-compose.minio.yml up -d
|
||||
# 控制台:http://127.0.0.1:9001 (默认账号 minioadmin / minioadmin,生产务必修改)
|
||||
|
||||
services:
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2024-11-07T00-52-20Z
|
||||
command: server /data --console-address ":9001"
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: minioadmin
|
||||
MINIO_ROOT_PASSWORD: minioadmin
|
||||
volumes:
|
||||
- minio-data:/data
|
||||
|
||||
volumes:
|
||||
minio-data:
|
||||
27
server/deploy/minio-spring-config.example.yml
Normal file
27
server/deploy/minio-spring-config.example.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
# Spring Boot 使用 MinIO 时,在「生产配置」里增加或改成以下内容。
|
||||
# 不要提交含真实生产密码的副本;交给运维时可另存为服务器上的 application-prod.yml。
|
||||
#
|
||||
# 启动示例:java -jar wuhan-saga-server.jar --spring.profiles.active=prod
|
||||
#
|
||||
# 与运维 Docker 中环境变量的对应:
|
||||
# MINIO_ROOT_USER -> minio.access-key
|
||||
# MINIO_ROOT_PASSWORD -> minio.secret-key
|
||||
# endpoint:Spring 所在机器能访问的 MinIO API(9000)。同机 Docker host 网络一般用 http://127.0.0.1:9000
|
||||
|
||||
# --- 下面粘贴进 application-prod.yml(或通过 Nacos/环境变量等价提供)---
|
||||
|
||||
spring:
|
||||
config:
|
||||
activate:
|
||||
on-profile: prod
|
||||
|
||||
upload:
|
||||
storage: minio
|
||||
path: uploads/
|
||||
|
||||
minio:
|
||||
endpoint: http://127.0.0.1:9000
|
||||
access-key: klp
|
||||
secret-key: ruoyi123
|
||||
bucket: wuhan-saga
|
||||
region: us-east-1
|
||||
@@ -84,6 +84,12 @@
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>8.5.13</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.wuhansaga.server;
|
||||
|
||||
import com.wuhansaga.server.config.MinioProperties;
|
||||
import com.wuhansaga.server.config.PortalSiteProperties;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
@@ -8,7 +9,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.wuhansaga.server.mapper")
|
||||
@EnableConfigurationProperties(PortalSiteProperties.class)
|
||||
@EnableConfigurationProperties({ PortalSiteProperties.class, MinioProperties.class })
|
||||
public class WuhanSagaApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.wuhansaga.server.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* MinIO(S3 兼容)接入;仅在 upload.storage=minio 时使用。
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "minio")
|
||||
public record MinioProperties(
|
||||
/** 例:http://127.0.0.1:9000 或 https://minio.example.com(无尾斜杠) */
|
||||
String endpoint,
|
||||
String accessKey,
|
||||
String secretKey,
|
||||
/** 桶名,需有读权限;上传会 put 到此桶 */
|
||||
String bucket,
|
||||
/** 区域,MinIO 单机常用 us-east-1 */
|
||||
String region
|
||||
) {
|
||||
public MinioProperties {
|
||||
if (region == null || region.isBlank()) {
|
||||
region = "us-east-1";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,9 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||
@Value("${upload.path:uploads/}")
|
||||
private String uploadPath;
|
||||
|
||||
@Value("${upload.storage:local}")
|
||||
private String uploadStorage;
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
@@ -26,6 +29,9 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
if ("minio".equalsIgnoreCase(uploadStorage)) {
|
||||
return;
|
||||
}
|
||||
File dir = new File(uploadPath);
|
||||
if (!dir.isAbsolute()) {
|
||||
dir = new File(System.getProperty("user.dir"), uploadPath);
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.wuhansaga.server.controller.portal;
|
||||
|
||||
import com.wuhansaga.server.storage.UploadStorage;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* MinIO 模式下由应用流式输出 /uploads/**;对象不存在时回退到 classpath bundled-uploads。
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(name = "upload.storage", havingValue = "minio")
|
||||
public class UploadServeController {
|
||||
|
||||
private final UploadStorage uploadStorage;
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
@GetMapping("/uploads/**")
|
||||
public ResponseEntity<org.springframework.core.io.InputStreamResource> serve(HttpServletRequest request)
|
||||
throws IOException {
|
||||
String rel = requestUriWithoutContext(request);
|
||||
if (!rel.startsWith("/uploads/")) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
Optional<UploadStorage.StoredObject> fromStore = uploadStorage.open(rel);
|
||||
if (fromStore.isPresent()) {
|
||||
UploadStorage.StoredObject o = fromStore.get();
|
||||
MediaType mt = MediaType.parseMediaType(o.contentType());
|
||||
var body = new org.springframework.core.io.InputStreamResource(o.inputStream()) {
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return o.size();
|
||||
}
|
||||
};
|
||||
return ResponseEntity.ok().contentType(mt).contentLength(o.size()).body(body);
|
||||
}
|
||||
|
||||
String tail = rel.substring("/uploads/".length());
|
||||
Resource bundled = resourceLoader.getResource("classpath:/static/bundled-uploads/" + tail);
|
||||
if (!bundled.exists() || !bundled.isReadable()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
InputStream in = bundled.getInputStream();
|
||||
long len = bundled.contentLength();
|
||||
MediaType mt = MediaType.APPLICATION_OCTET_STREAM;
|
||||
String name = bundled.getFilename();
|
||||
if (name != null && (name.endsWith(".jpg") || name.endsWith(".jpeg"))) {
|
||||
mt = MediaType.IMAGE_JPEG;
|
||||
} else if (name != null && name.endsWith(".png")) {
|
||||
mt = MediaType.IMAGE_PNG;
|
||||
} else if (name != null && name.endsWith(".gif")) {
|
||||
mt = MediaType.IMAGE_GIF;
|
||||
} else if (name != null && name.endsWith(".webp")) {
|
||||
mt = MediaType.parseMediaType("image/webp");
|
||||
}
|
||||
var body = new org.springframework.core.io.InputStreamResource(in) {
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return len > 0 ? len : -1;
|
||||
}
|
||||
};
|
||||
if (len > 0) {
|
||||
return ResponseEntity.ok().contentType(mt).contentLength(len).body(body);
|
||||
}
|
||||
return ResponseEntity.ok().contentType(mt).body(body);
|
||||
}
|
||||
|
||||
private static String requestUriWithoutContext(HttpServletRequest request) {
|
||||
String uri = request.getRequestURI();
|
||||
String cp = request.getContextPath();
|
||||
if (cp != null && !cp.isEmpty() && uri.startsWith(cp)) {
|
||||
return uri.substring(cp.length());
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,11 @@ import com.wuhansaga.server.common.PageQuery;
|
||||
import com.wuhansaga.server.common.PageResult;
|
||||
import com.wuhansaga.server.entity.MediaLibrary;
|
||||
import com.wuhansaga.server.mapper.MediaLibraryMapper;
|
||||
import com.wuhansaga.server.storage.UploadStorage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@@ -20,29 +18,11 @@ import java.util.UUID;
|
||||
public class MediaLibraryService {
|
||||
|
||||
private final MediaLibraryMapper mediaLibraryMapper;
|
||||
private final UploadStorage uploadStorage;
|
||||
|
||||
@Value("${upload.path}")
|
||||
private String uploadPath;
|
||||
|
||||
private File uploadDir;
|
||||
|
||||
public MediaLibraryService(MediaLibraryMapper mediaLibraryMapper) {
|
||||
public MediaLibraryService(MediaLibraryMapper mediaLibraryMapper, UploadStorage uploadStorage) {
|
||||
this.mediaLibraryMapper = mediaLibraryMapper;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
uploadDir = new File(uploadPath);
|
||||
if (!uploadDir.isAbsolute()) {
|
||||
String userDir = System.getProperty("user.dir");
|
||||
uploadDir = new File(userDir, uploadPath);
|
||||
}
|
||||
if (!uploadDir.exists()) {
|
||||
boolean created = uploadDir.mkdirs();
|
||||
log.info("初始化上传目录: {} => {}", uploadDir.getAbsolutePath(), created);
|
||||
} else {
|
||||
log.info("上传目录已存在: {}", uploadDir.getAbsolutePath());
|
||||
}
|
||||
this.uploadStorage = uploadStorage;
|
||||
}
|
||||
|
||||
public PageResult<MediaLibrary> page(String category, String keyword, PageQuery query) {
|
||||
@@ -63,15 +43,7 @@ public class MediaLibraryService {
|
||||
String newFileName = UUID.randomUUID().toString().replace("-", "") + ext;
|
||||
String relativePath = "/uploads/" + category + "/" + newFileName;
|
||||
|
||||
File categoryDir = new File(uploadDir, category);
|
||||
if (!categoryDir.exists()) {
|
||||
boolean created = categoryDir.mkdirs();
|
||||
log.info("创建分类目录: {} => {}", categoryDir.getAbsolutePath(), created);
|
||||
}
|
||||
|
||||
File dest = new File(categoryDir, newFileName);
|
||||
file.transferTo(dest);
|
||||
log.info("文件已保存: {}", dest.getAbsolutePath());
|
||||
uploadStorage.store(file, relativePath);
|
||||
|
||||
MediaLibrary media = new MediaLibrary();
|
||||
media.setFilePath(relativePath);
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.wuhansaga.server.storage;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(name = "upload.storage", havingValue = "local", matchIfMissing = true)
|
||||
public class LocalUploadStorage implements UploadStorage {
|
||||
|
||||
@Value("${upload.path}")
|
||||
private String uploadPath;
|
||||
|
||||
private File uploadDir;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
uploadDir = new File(uploadPath);
|
||||
if (!uploadDir.isAbsolute()) {
|
||||
uploadDir = new File(System.getProperty("user.dir"), uploadPath);
|
||||
}
|
||||
if (!uploadDir.exists()) {
|
||||
boolean created = uploadDir.mkdirs();
|
||||
log.info("初始化本地上传目录: {} => {}", uploadDir.getAbsolutePath(), created);
|
||||
} else {
|
||||
log.info("本地上传目录已存在: {}", uploadDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(MultipartFile file, String relativePath) throws IOException {
|
||||
if (relativePath == null || !relativePath.startsWith("/uploads/")) {
|
||||
throw new IllegalArgumentException("relativePath 须以 /uploads/ 开头: " + relativePath);
|
||||
}
|
||||
String withoutPrefix = relativePath.substring("/uploads/".length());
|
||||
int slash = withoutPrefix.lastIndexOf('/');
|
||||
String category = slash >= 0 ? withoutPrefix.substring(0, slash) : "";
|
||||
String fileName = slash >= 0 ? withoutPrefix.substring(slash + 1) : withoutPrefix;
|
||||
|
||||
File categoryDir = category.isEmpty() ? uploadDir : new File(uploadDir, category);
|
||||
if (!categoryDir.exists()) {
|
||||
boolean created = categoryDir.mkdirs();
|
||||
log.info("创建分类目录: {} => {}", categoryDir.getAbsolutePath(), created);
|
||||
}
|
||||
File dest = new File(categoryDir, fileName);
|
||||
file.transferTo(dest);
|
||||
log.info("文件已保存(本地): {}", dest.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<StoredObject> open(String relativePath) throws IOException {
|
||||
if (relativePath == null || !relativePath.startsWith("/uploads/")) {
|
||||
return Optional.empty();
|
||||
}
|
||||
String withoutPrefix = relativePath.substring("/uploads/".length());
|
||||
File f = new File(uploadDir, withoutPrefix.replace('/', File.separatorChar));
|
||||
if (!f.isFile()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
String ct = Files.probeContentType(f.toPath());
|
||||
if (ct == null) {
|
||||
ct = "application/octet-stream";
|
||||
}
|
||||
InputStream in = Files.newInputStream(f.toPath());
|
||||
return Optional.of(new StoredObject(in, f.length(), ct));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.wuhansaga.server.storage;
|
||||
|
||||
import com.wuhansaga.server.config.MinioProperties;
|
||||
import io.minio.BucketExistsArgs;
|
||||
import io.minio.GetObjectArgs;
|
||||
import io.minio.MakeBucketArgs;
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.PutObjectArgs;
|
||||
import io.minio.StatObjectArgs;
|
||||
import io.minio.StatObjectResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(name = "upload.storage", havingValue = "minio")
|
||||
public class MinioUploadStorage implements UploadStorage {
|
||||
|
||||
private final MinioProperties props;
|
||||
private MinioClient client;
|
||||
|
||||
public MinioUploadStorage(MinioProperties props) {
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() throws java.io.IOException {
|
||||
if (props.endpoint() == null || props.endpoint().isBlank()) {
|
||||
throw new IllegalStateException("minio.endpoint 未配置");
|
||||
}
|
||||
if (props.accessKey() == null || props.secretKey() == null || props.bucket() == null
|
||||
|| props.accessKey().isBlank() || props.secretKey().isBlank() || props.bucket().isBlank()) {
|
||||
throw new IllegalStateException("minio.access-key、secret-key、bucket 均需配置");
|
||||
}
|
||||
client = MinioClient.builder()
|
||||
.endpoint(props.endpoint())
|
||||
.region(props.region())
|
||||
.credentials(props.accessKey(), props.secretKey())
|
||||
.build();
|
||||
try {
|
||||
boolean exists = client.bucketExists(BucketExistsArgs.builder().bucket(props.bucket()).build());
|
||||
if (!exists) {
|
||||
client.makeBucket(MakeBucketArgs.builder().bucket(props.bucket()).build());
|
||||
log.info("已创建 MinIO 桶: {}", props.bucket());
|
||||
} else {
|
||||
log.info("MinIO 桶已存在: {}", props.bucket());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new java.io.IOException("初始化 MinIO 失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String objectName(String relativePath) {
|
||||
if (relativePath == null || !relativePath.startsWith("/uploads/")) {
|
||||
throw new IllegalArgumentException("relativePath 须以 /uploads/ 开头: " + relativePath);
|
||||
}
|
||||
return relativePath.substring(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(MultipartFile file, String relativePath) throws java.io.IOException {
|
||||
String name = objectName(relativePath);
|
||||
String ct = file.getContentType();
|
||||
if (ct == null || ct.isBlank()) {
|
||||
ct = "application/octet-stream";
|
||||
}
|
||||
try (InputStream in = file.getInputStream()) {
|
||||
client.putObject(
|
||||
PutObjectArgs.builder()
|
||||
.bucket(props.bucket())
|
||||
.object(name)
|
||||
.stream(in, file.getSize(), -1)
|
||||
.contentType(ct)
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
throw new java.io.IOException("MinIO 上传失败: " + e.getMessage(), e);
|
||||
}
|
||||
log.info("文件已写入 MinIO: {}/{}", props.bucket(), name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<StoredObject> open(String relativePath) throws java.io.IOException {
|
||||
if (relativePath == null || !relativePath.startsWith("/uploads/")) {
|
||||
return Optional.empty();
|
||||
}
|
||||
String name = objectName(relativePath);
|
||||
try {
|
||||
StatObjectResponse stat = client.statObject(
|
||||
StatObjectArgs.builder().bucket(props.bucket()).object(name).build());
|
||||
InputStream in = client.getObject(
|
||||
GetObjectArgs.builder().bucket(props.bucket()).object(name).build());
|
||||
String ct = stat.contentType();
|
||||
if (ct == null || ct.isBlank()) {
|
||||
ct = "application/octet-stream";
|
||||
}
|
||||
return Optional.of(new StoredObject(in, stat.size(), ct));
|
||||
} catch (io.minio.errors.ErrorResponseException e) {
|
||||
if ("NoSuchKey".equals(e.errorResponse().code()) || "NoSuchObject".equals(e.errorResponse().code())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
throw new java.io.IOException("MinIO 读取失败: " + e.getMessage(), e);
|
||||
} catch (Exception e) {
|
||||
throw new java.io.IOException("MinIO 读取失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.wuhansaga.server.storage;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 统一上传存储;相对路径与库存一致,如 /uploads/banner/xxx.jpg
|
||||
*/
|
||||
public interface UploadStorage {
|
||||
|
||||
void store(MultipartFile file, String relativePath) throws IOException;
|
||||
|
||||
Optional<StoredObject> open(String relativePath) throws IOException;
|
||||
|
||||
record StoredObject(InputStream inputStream, long size, String contentType) {}
|
||||
}
|
||||
@@ -56,11 +56,21 @@ knife4j:
|
||||
setting:
|
||||
language: zh_cn
|
||||
|
||||
# 存储:local=本机 uploads | minio=对象存储,见 server/deploy/minio-spring-config.example.yml
|
||||
upload:
|
||||
storage: local
|
||||
path: uploads/
|
||||
allowed-types: image/jpeg,image/png,image/gif,image/webp,image/svg+xml,video/mp4,video/webm
|
||||
max-size: 52428800
|
||||
|
||||
# 仅 upload.storage=minio 时生效;密钥用环境变量注入,勿提交生产明文
|
||||
minio:
|
||||
endpoint: http://127.0.0.1:9000
|
||||
access-key: minioadmin
|
||||
secret-key: minioadmin
|
||||
bucket: wuhan-saga
|
||||
region: us-east-1
|
||||
|
||||
# 新闻中心多站点:单部署实例默认站点;扩展编码时改 allowed-site-codes 与库内数据
|
||||
app:
|
||||
portal:
|
||||
|
||||
@@ -56,11 +56,21 @@ knife4j:
|
||||
setting:
|
||||
language: zh_cn
|
||||
|
||||
# 存储:local=本机 uploads | minio=对象存储,见 server/deploy/minio-spring-config.example.yml
|
||||
upload:
|
||||
storage: local
|
||||
path: uploads/
|
||||
allowed-types: image/jpeg,image/png,image/gif,image/webp,image/svg+xml,video/mp4,video/webm
|
||||
max-size: 52428800
|
||||
|
||||
# 仅 upload.storage=minio 时生效;密钥用环境变量注入,勿提交生产明文
|
||||
minio:
|
||||
endpoint: http://127.0.0.1:9000
|
||||
access-key: minioadmin
|
||||
secret-key: minioadmin
|
||||
bucket: wuhan-saga
|
||||
region: us-east-1
|
||||
|
||||
# 新闻中心多站点:单部署实例默认站点;扩展编码时改 allowed-site-codes 与库内数据
|
||||
app:
|
||||
portal:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,10 +7,12 @@ com\wuhansaga\server\mapper\SingleEquipmentMapper.class
|
||||
com\wuhansaga\server\mapper\ProductCategoryMapper.class
|
||||
com\wuhansaga\server\service\WorkshopService.class
|
||||
com\wuhansaga\server\controller\portal\PortalCaseController.class
|
||||
com\wuhansaga\server\config\MinioProperties.class
|
||||
com\wuhansaga\server\service\CompanyInfoService.class
|
||||
com\wuhansaga\server\controller\admin\AdminBannerController.class
|
||||
com\wuhansaga\server\controller\admin\AdminCaseController.class
|
||||
com\wuhansaga\server\mapper\MediaLibraryMapper.class
|
||||
com\wuhansaga\server\controller\portal\UploadServeController$1.class
|
||||
com\wuhansaga\server\common\PageQuery.class
|
||||
com\wuhansaga\server\entity\MediaLibrary.class
|
||||
com\wuhansaga\server\controller\portal\PortalWorkshopController.class
|
||||
@@ -23,11 +25,15 @@ com\wuhansaga\server\service\MediaLibraryService.class
|
||||
com\wuhansaga\server\mapper\CaseMediaMapper.class
|
||||
com\wuhansaga\server\config\OpenApiConfig.class
|
||||
com\wuhansaga\server\mapper\CompanyInfoMapper.class
|
||||
com\wuhansaga\server\controller\portal\UploadServeController.class
|
||||
com\wuhansaga\server\entity\CaseMedia.class
|
||||
com\wuhansaga\server\controller\admin\AdminAuthController.class
|
||||
com\wuhansaga\server\entity\ProductMedia.class
|
||||
com\wuhansaga\server\controller\portal\UploadServeController$2.class
|
||||
com\wuhansaga\server\mapper\SysUserMapper.class
|
||||
com\wuhansaga\server\storage\UploadStorage$StoredObject.class
|
||||
com\wuhansaga\server\mapper\AboutMapper.class
|
||||
com\wuhansaga\server\storage\LocalUploadStorage.class
|
||||
com\wuhansaga\server\controller\admin\AdminProductCategoryController.class
|
||||
com\wuhansaga\server\controller\portal\PortalProductController.class
|
||||
com\wuhansaga\server\common\GlobalExceptionHandler.class
|
||||
@@ -48,6 +54,7 @@ com\wuhansaga\server\controller\admin\AdminCompanyController.class
|
||||
com\wuhansaga\server\entity\SysUser.class
|
||||
com\wuhansaga\server\service\AboutService.class
|
||||
com\wuhansaga\server\mapper\CaseStudyMapper.class
|
||||
com\wuhansaga\server\storage\UploadStorage.class
|
||||
com\wuhansaga\server\config\WebMvcConfig.class
|
||||
com\wuhansaga\server\entity\News.class
|
||||
com\wuhansaga\server\common\PageResult.class
|
||||
@@ -79,6 +86,7 @@ com\wuhansaga\server\controller\admin\AdminWorkshopController.class
|
||||
com\wuhansaga\server\mapper\CaseCategoryMapper.class
|
||||
com\wuhansaga\server\entity\Banner.class
|
||||
com\wuhansaga\server\entity\ProductCategory.class
|
||||
com\wuhansaga\server\storage\MinioUploadStorage.class
|
||||
com\wuhansaga\server\entity\Contact.class
|
||||
com\wuhansaga\server\config\SaTokenConfig.class
|
||||
com\wuhansaga\server\entity\SparePart.class
|
||||
|
||||
@@ -3,6 +3,7 @@ D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\common\PageQuery.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\common\PageResult.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\common\R.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\config\MinioProperties.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\config\OpenApiConfig.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\config\PortalSiteProperties.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\config\PortalSiteResolver.java
|
||||
@@ -31,6 +32,7 @@ D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\controller\portal\PortalProductController.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\controller\portal\PortalTechnologyController.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\controller\portal\PortalWorkshopController.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\controller\portal\UploadServeController.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\entity\About.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\entity\Banner.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\entity\CaseCategory.java
|
||||
@@ -83,4 +85,7 @@ D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\service\SingleEquipmentService.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\service\SparePartService.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\service\WorkshopService.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\storage\LocalUploadStorage.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\storage\MinioUploadStorage.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\storage\UploadStorage.java
|
||||
D:\DeXun_workspace\projects\wuhan-saga-official-website\server\src\main\java\com\wuhansaga\server\WuhanSagaApplication.java
|
||||
|
||||
Reference in New Issue
Block a user