minio
This commit is contained in:
commit
fbfed73116
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
4
.idea/encodings.xml
Normal file
4
.idea/encodings.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" native2AsciiForPropertiesFiles="true" defaultCharsetForPropertiesFiles="UTF-8" />
|
||||
</project>
|
68
.idea/misc.xml
Normal file
68
.idea/misc.xml
Normal file
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectInspectionProfilesVisibleTreeState">
|
||||
<entry key="Project Default">
|
||||
<profile-state>
|
||||
<expanded-state>
|
||||
<State />
|
||||
<State>
|
||||
<id>JVM 语言</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>Java</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>Java 10Java 语言级别迁移帮助Java</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>Java 11Java 语言级别迁移帮助Java</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>Java 14Java 语言级别迁移帮助Java</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>Java 15Java 语言级别迁移帮助Java</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>Java 5Java 语言级别迁移帮助Java</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>Java 7Java 语言级别迁移帮助Java</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>Java 8Java 语言级别迁移帮助Java</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>Java 9Java 语言级别迁移帮助Java</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>Java 互操作问题Kotlin</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>Java 语言级别迁移帮助Java</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>JavaScript and TypeScript</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>Kotlin</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>有效性问题JavaScript and TypeScript</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>模块化问题Java</id>
|
||||
</State>
|
||||
<State>
|
||||
<id>迁移Kotlin</id>
|
||||
</State>
|
||||
</expanded-state>
|
||||
<selected-state>
|
||||
<State>
|
||||
<id>Android</id>
|
||||
</State>
|
||||
</selected-state>
|
||||
</profile-state>
|
||||
</entry>
|
||||
</component>
|
||||
</project>
|
33
minio-admin/.gitignore
vendored
Normal file
33
minio-admin/.gitignore
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
0
minio-admin/logs/error.log
Normal file
0
minio-admin/logs/error.log
Normal file
48
minio-admin/logs/info.log
Normal file
48
minio-admin/logs/info.log
Normal file
@ -0,0 +1,48 @@
|
||||
11:56:44.842 [background-preinit] INFO o.h.v.i.util.Version - [<clinit>,21] - HV000001: Hibernate Validator 8.0.0.Final
|
||||
11:56:44.859 [restartedMain] INFO c.m.MinioUploadFileApplication - [logStarting,51] - Starting MinioUploadFileApplication using Java 19.0.1 with PID 18717 (/Users/lan/minio-project-master/minio-admin/target/classes started by lan in /Users/lan/minio-project-master/minio-admin)
|
||||
11:56:44.861 [restartedMain] INFO c.m.MinioUploadFileApplication - [logStartupProfileInfo,630] - No active profile set, falling back to 1 default profile: "default"
|
||||
11:56:47.883 [restartedMain] INFO o.a.c.h.Http11NioProtocol - [log,173] - Initializing ProtocolHandler ["http-nio-9090"]
|
||||
11:56:47.885 [restartedMain] INFO o.a.c.c.StandardService - [log,173] - Starting service [Tomcat]
|
||||
11:56:47.885 [restartedMain] INFO o.a.c.c.StandardEngine - [log,173] - Starting Servlet engine: [Apache Tomcat/10.1.5]
|
||||
11:56:47.954 [restartedMain] INFO o.a.c.c.C.[.[.[/] - [log,173] - Initializing Spring embedded WebApplicationContext
|
||||
11:56:49.612 [restartedMain] INFO o.a.c.h.Http11NioProtocol - [log,173] - Starting ProtocolHandler ["http-nio-9090"]
|
||||
11:56:49.642 [restartedMain] INFO c.m.MinioUploadFileApplication - [logStarted,57] - Started MinioUploadFileApplication in 5.269 seconds (process running for 7.524)
|
||||
12:14:01.766 [http-nio-9090-exec-1] INFO o.a.c.c.C.[.[.[/] - [log,173] - Initializing Spring DispatcherServlet 'dispatcherServlet'
|
||||
12:14:01.881 [http-nio-9090-exec-1] INFO c.m.c.FileMinioController - [checkFileUploadedByMd5,74] - REST: 通过查询 <036ee500fdb624314479780c6daf547d> 文件是否存在、是否进行断点续传
|
||||
12:14:01.882 [http-nio-9090-exec-1] INFO c.m.s.i.UploadServiceImpl - [getByFileMD5,48] - tip message: 通过 <036ee500fdb624314479780c6daf547d> 查询redis是否存在
|
||||
12:14:02.490 [http-nio-9090-exec-1] INFO c.m.s.i.UploadServiceImpl - [getByFileMD5,59] - tip message: 通过 <036ee500fdb624314479780c6daf547d> 查询mysql是否存在
|
||||
12:14:02.681 [http-nio-9090-exec-1] INFO c.z.h.HikariDataSource - [getConnection,110] - HikariPool-1 - Starting...
|
||||
12:14:02.962 [http-nio-9090-exec-1] INFO c.z.h.p.HikariPool - [checkFailFast,565] - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@1b0794cd
|
||||
12:14:02.964 [http-nio-9090-exec-1] INFO c.z.h.HikariDataSource - [getConnection,123] - HikariPool-1 - Start completed.
|
||||
12:14:03.297 [http-nio-9090-exec-2] INFO c.m.c.FileMinioController - [initMultiPartUpload,90] - REST: 通过 <FileUploadInfo(fileName=image.png, fileSize=1680162, contentType=application/octet-stream, chunkNum=1, uploadId=null, chunkSize=10485760, fileMd5=036ee500fdb624314479780c6daf547d, fileType=image, chunkUploadedList=[])> 初始化上传任务
|
||||
12:14:03.301 [http-nio-9090-exec-2] INFO c.m.s.i.UploadServiceImpl - [initMultiPartUpload,85] - tip message: 通过 <FileUploadInfo(fileName=image.png, fileSize=1680162, contentType=application/octet-stream, chunkNum=1, uploadId=null, chunkSize=10485760, fileMd5=036ee500fdb624314479780c6daf547d, fileType=image, chunkUploadedList=[])> 开始初始化<分片上传>任务
|
||||
12:14:03.834 [http-nio-9090-exec-2] INFO c.m.s.i.UploadServiceImpl - [initMultiPartUpload,91] - tip message: 当前分片数量 <1> 进行单文件上传
|
||||
12:14:03.847 [http-nio-9090-exec-2] INFO c.m.c.MybatisPlusConfig - [insertFill,37] - start insert fill ....
|
||||
12:14:03.941 [http-nio-9090-exec-2] INFO c.m.utils.MinioUtils - [getUploadObjectUrl,64] - tip message: 通过 <036ee500fdb624314479780c6daf547d.png-image> 开始单文件上传<minio>
|
||||
12:14:03.947 [http-nio-9090-exec-2] INFO c.m.utils.MinioUtils - [getUploadObjectUrl,74] - tip message: 单个文件上传、成功
|
||||
12:18:57.075 [SpringApplicationShutdownHook] INFO c.z.h.HikariDataSource - [close,350] - HikariPool-1 - Shutdown initiated...
|
||||
12:18:57.082 [SpringApplicationShutdownHook] INFO c.z.h.HikariDataSource - [close,352] - HikariPool-1 - Shutdown completed.
|
||||
12:19:02.637 [background-preinit] INFO o.h.v.i.util.Version - [<clinit>,21] - HV000001: Hibernate Validator 8.0.0.Final
|
||||
12:19:02.653 [restartedMain] INFO c.m.MinioUploadFileApplication - [logStarting,51] - Starting MinioUploadFileApplication using Java 19.0.1 with PID 20456 (/Users/lan/minio-project-master/minio-admin/target/classes started by lan in /Users/lan/minio-project-master/minio-admin)
|
||||
12:19:02.654 [restartedMain] INFO c.m.MinioUploadFileApplication - [logStartupProfileInfo,630] - No active profile set, falling back to 1 default profile: "default"
|
||||
12:19:04.851 [restartedMain] INFO o.a.c.h.Http11NioProtocol - [log,173] - Initializing ProtocolHandler ["http-nio-9090"]
|
||||
12:19:04.851 [restartedMain] INFO o.a.c.c.StandardService - [log,173] - Starting service [Tomcat]
|
||||
12:19:04.852 [restartedMain] INFO o.a.c.c.StandardEngine - [log,173] - Starting Servlet engine: [Apache Tomcat/10.1.5]
|
||||
12:19:04.915 [restartedMain] INFO o.a.c.c.C.[.[.[/] - [log,173] - Initializing Spring embedded WebApplicationContext
|
||||
12:19:06.455 [restartedMain] INFO o.a.c.h.Http11NioProtocol - [log,173] - Starting ProtocolHandler ["http-nio-9090"]
|
||||
12:19:06.558 [restartedMain] INFO c.m.MinioUploadFileApplication - [logStarted,57] - Started MinioUploadFileApplication in 4.334 seconds (process running for 5.695)
|
||||
12:19:30.858 [http-nio-9090-exec-1] INFO o.a.c.c.C.[.[.[/] - [log,173] - Initializing Spring DispatcherServlet 'dispatcherServlet'
|
||||
12:19:30.918 [http-nio-9090-exec-1] INFO c.m.c.FileMinioController - [checkFileUploadedByMd5,74] - REST: 通过查询 <dcf813524ee0f39b6d73aba673f2fe2d> 文件是否存在、是否进行断点续传
|
||||
12:19:30.919 [http-nio-9090-exec-1] INFO c.m.s.i.UploadServiceImpl - [getByFileMD5,48] - tip message: 通过 <dcf813524ee0f39b6d73aba673f2fe2d> 查询redis是否存在
|
||||
12:19:31.237 [http-nio-9090-exec-1] INFO c.m.s.i.UploadServiceImpl - [getByFileMD5,59] - tip message: 通过 <dcf813524ee0f39b6d73aba673f2fe2d> 查询mysql是否存在
|
||||
12:19:31.343 [http-nio-9090-exec-1] INFO c.z.h.HikariDataSource - [getConnection,110] - HikariPool-1 - Starting...
|
||||
12:19:31.551 [http-nio-9090-exec-1] INFO c.z.h.p.HikariPool - [checkFailFast,565] - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@42d42417
|
||||
12:19:31.552 [http-nio-9090-exec-1] INFO c.z.h.HikariDataSource - [getConnection,123] - HikariPool-1 - Start completed.
|
||||
12:19:31.708 [http-nio-9090-exec-2] INFO c.m.c.FileMinioController - [initMultiPartUpload,90] - REST: 通过 <FileUploadInfo(fileName=院徽.png, fileSize=26909, contentType=application/octet-stream, chunkNum=1, uploadId=null, chunkSize=10485760, fileMd5=dcf813524ee0f39b6d73aba673f2fe2d, fileType=image, chunkUploadedList=[])> 初始化上传任务
|
||||
12:19:31.712 [http-nio-9090-exec-2] INFO c.m.s.i.UploadServiceImpl - [initMultiPartUpload,85] - tip message: 通过 <FileUploadInfo(fileName=院徽.png, fileSize=26909, contentType=application/octet-stream, chunkNum=1, uploadId=null, chunkSize=10485760, fileMd5=dcf813524ee0f39b6d73aba673f2fe2d, fileType=image, chunkUploadedList=[])> 开始初始化<分片上传>任务
|
||||
12:19:32.184 [http-nio-9090-exec-2] INFO c.m.s.i.UploadServiceImpl - [initMultiPartUpload,91] - tip message: 当前分片数量 <1> 进行单文件上传
|
||||
12:19:32.202 [http-nio-9090-exec-2] INFO c.m.c.MybatisPlusConfig - [insertFill,37] - start insert fill ....
|
||||
12:19:32.279 [http-nio-9090-exec-2] INFO c.m.utils.MinioUtils - [getUploadObjectUrl,64] - tip message: 通过 <dcf813524ee0f39b6d73aba673f2fe2d.png-image> 开始单文件上传<minio>
|
||||
12:19:32.287 [http-nio-9090-exec-2] INFO c.m.utils.MinioUtils - [getUploadObjectUrl,74] - tip message: 单个文件上传、成功
|
||||
12:35:11.549 [SpringApplicationShutdownHook] INFO c.z.h.HikariDataSource - [close,350] - HikariPool-1 - Shutdown initiated...
|
||||
12:35:11.575 [SpringApplicationShutdownHook] INFO c.z.h.HikariDataSource - [close,352] - HikariPool-1 - Shutdown completed.
|
104
minio-admin/pom.xml
Normal file
104
minio-admin/pom.xml
Normal file
@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.mmg</groupId>
|
||||
<artifactId>minio-admin</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>minio-admin</name>
|
||||
<description>minio-admin</description>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<spring-boot.version>3.0.2</spring-boot.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.50</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.5.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>8.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.7.20</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,11 @@
|
||||
package com.mmg;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MinioUploadFileApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MinioUploadFileApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.mmg.config;
|
||||
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import io.minio.CreateMultipartUploadResponse;
|
||||
import io.minio.ListPartsResponse;
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.ObjectWriteResponse;
|
||||
import io.minio.messages.Part;
|
||||
|
||||
public class CustomMinioClient extends MinioClient {
|
||||
|
||||
/**
|
||||
* 继承父类
|
||||
* @param client
|
||||
*/
|
||||
public CustomMinioClient(MinioClient client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 初始化分片上传、获取 uploadId
|
||||
*
|
||||
* @param bucket String 存储桶名称
|
||||
* @param region String
|
||||
* @param object String 文件名称
|
||||
* @param headers Multimap<String, String> 请求头
|
||||
* @param extraQueryParams Multimap<String, String>
|
||||
* @return String
|
||||
*/
|
||||
public String initMultiPartUpload(String bucket, String region, String object, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) throws Exception {
|
||||
CreateMultipartUploadResponse response = this.createMultipartUpload(bucket, region, object, headers, extraQueryParams);
|
||||
return response.result().uploadId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并分片
|
||||
*
|
||||
* @param bucketName String 桶名称
|
||||
* @param region String
|
||||
* @param objectName String 文件名称
|
||||
* @param uploadId String 上传的 uploadId
|
||||
* @param parts Part[] 分片集合
|
||||
* @param extraHeaders Multimap<String, String>
|
||||
* @param extraQueryParams Multimap<String, String>
|
||||
* @return ObjectWriteResponse
|
||||
*/
|
||||
public ObjectWriteResponse mergeMultipartUpload(String bucketName, String region, String objectName, String uploadId, Part[] parts, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws Exception {
|
||||
return this.completeMultipartUpload(bucketName, region, objectName, uploadId, parts, extraHeaders, extraQueryParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前上传后的分片信息
|
||||
*
|
||||
* @param bucketName String 桶名称
|
||||
* @param region String
|
||||
* @param objectName String 文件名称
|
||||
* @param maxParts Integer 分片数量
|
||||
* @param partNumberMarker Integer 分片起始值
|
||||
* @param uploadId String 上传的 uploadId
|
||||
* @param extraHeaders Multimap<String, String>
|
||||
* @param extraQueryParams Multimap<String, String>
|
||||
* @return ListPartsResponse
|
||||
*/
|
||||
public ListPartsResponse listMultipart(String bucketName, String region, String objectName, Integer maxParts, Integer partNumberMarker, String uploadId, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws Exception {
|
||||
return this.listParts(bucketName, region, objectName, maxParts, partNumberMarker, uploadId, extraHeaders, extraQueryParams);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,52 @@
|
||||
package com.mmg.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* MybatisPlus配置类
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@MapperScan("com.mmg.mapper")
|
||||
public class MybatisPlusConfig implements MetaObjectHandler {
|
||||
/**
|
||||
* 新的分页插件
|
||||
* 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
|
||||
* 避免缓存出现问题(该属性会在旧插件移除后一同移除)
|
||||
*/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
log.info("start insert fill ....");
|
||||
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
|
||||
this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)
|
||||
/* 上面选其一使用,下面的已过时(注意 strictInsertFill 有多个方法,详细查看源码) */
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
log.info("start update fill ....");
|
||||
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
|
||||
this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)
|
||||
/* 上面选其一使用,下面的已过时(注意 strictUpdateFill 有多个方法,详细查看源码) */
|
||||
}
|
||||
|
||||
}
|
48
minio-admin/src/main/java/com/mmg/config/RedisConfig.java
Normal file
48
minio-admin/src/main/java/com/mmg/config/RedisConfig.java
Normal file
@ -0,0 +1,48 @@
|
||||
package com.mmg.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
//编写我们自己的RedisTemplate
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
//固定redis模板,在工作中,拿去就可以用
|
||||
@Bean
|
||||
@SuppressWarnings("all")
|
||||
//改成String,Object类型
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
||||
//我们为了自己开发方便,一般直接使用<String, Object>
|
||||
RedisTemplate<String, Object> template = new RedisTemplate();
|
||||
template.setConnectionFactory(factory);
|
||||
|
||||
//Json序列化配置
|
||||
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
jackson2JsonRedisSerializer.setObjectMapper(om);
|
||||
//String 的序列化
|
||||
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
|
||||
|
||||
//key采用string的序列化方式
|
||||
template.setKeySerializer(stringRedisSerializer);
|
||||
//value的序列化方式采用jackson
|
||||
template.setValueSerializer(jackson2JsonRedisSerializer);
|
||||
//hash的key也采用String的序列化方式
|
||||
template.setHashKeySerializer(stringRedisSerializer);
|
||||
//hash的value序列化方式采用jackson
|
||||
template.setHashValueSerializer(jackson2JsonRedisSerializer);
|
||||
template.afterPropertiesSet();
|
||||
|
||||
return template;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.mmg.config;
|
||||
|
||||
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import com.alibaba.fastjson.support.config.FastJsonConfig;
|
||||
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
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.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
|
||||
/**
|
||||
* @description Web应用程序配置
|
||||
* @author LGY
|
||||
* @date 2023/03/14 20:09
|
||||
* @version 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
public class WebAppConfigurer implements WebMvcConfigurer {
|
||||
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowCredentials(true)
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
|
||||
.allowedHeaders("*");
|
||||
}
|
||||
|
||||
//实体类属性为空时不进行序列化返回给前端
|
||||
@Bean
|
||||
public HttpMessageConverters fastJsonHttpMessageConverters() {
|
||||
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
|
||||
FastJsonConfig fastJsonConfig = new FastJsonConfig();
|
||||
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
|
||||
fastConverter.setFastJsonConfig(fastJsonConfig);
|
||||
HttpMessageConverter<?> converter = fastConverter;
|
||||
return new HttpMessageConverters(converter);
|
||||
}
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
package com.mmg.controller;
|
||||
|
||||
|
||||
import com.mmg.model.vo.FileUploadInfo;
|
||||
import com.mmg.service.UploadService;
|
||||
import com.mmg.utils.MinioUtils;
|
||||
import com.mmg.utils.R;
|
||||
import com.mmg.utils.RedisUtil;
|
||||
import com.mmg.utils.RespEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* minio上传流程
|
||||
* <p>
|
||||
* 1.检查数据库中是否存在上传文件
|
||||
* <p>
|
||||
* 2.根据文件信息初始化,获取分片预签名url地址,前端根据url地址上传文件
|
||||
* <p>
|
||||
* 3.上传完成后,将分片上传的文件进行合并
|
||||
* <p>
|
||||
* 4.保存文件信息到数据库
|
||||
*/
|
||||
@RestController
|
||||
@Slf4j
|
||||
@RequestMapping("upload")
|
||||
public class FileMinioController {
|
||||
|
||||
@Resource
|
||||
private UploadService uploadService;
|
||||
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Resource
|
||||
private MinioUtils minioUtils;
|
||||
|
||||
/**
|
||||
* @param fileMD5 文件md5
|
||||
* @return {@link R }
|
||||
* @description 获取上传文件
|
||||
* @author LGY
|
||||
* @date 2023/04/26 16:00
|
||||
*/
|
||||
@GetMapping("/getUploadingFile/{fileMD5}")
|
||||
public R getUploadingFile(@PathVariable String fileMD5) {
|
||||
try {
|
||||
if (StringUtils.isEmpty(fileMD5)) {
|
||||
return R.error();
|
||||
}
|
||||
FileUploadInfo fileUploadInfo = (FileUploadInfo) redisUtil.get(fileMD5);
|
||||
if (fileUploadInfo != null) {
|
||||
// 查询上传后的分片数据
|
||||
fileUploadInfo.setChunkUploadedList(minioUtils.getChunkByFileMD5(fileUploadInfo.getFileName(), fileUploadInfo.getUploadId(), fileUploadInfo.getFileType()));
|
||||
return R.ok().setData(fileUploadInfo);
|
||||
}
|
||||
return R.error();
|
||||
} catch (Exception e) {
|
||||
return R.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验文件是否存在
|
||||
*
|
||||
* @param md5 String
|
||||
* @return ResponseResult<Object>
|
||||
*/
|
||||
@GetMapping("/multipart/check")
|
||||
public R checkFileUploadedByMd5(@RequestParam("md5") String md5) {
|
||||
log.info("REST: 通过查询 <{}> 文件是否存在、是否进行断点续传", md5);
|
||||
if (StringUtils.isEmpty(md5)) {
|
||||
log.error("查询文件是否存在、入参无效");
|
||||
return R.error(RespEnum.ACCESS_PARAMETER_INVALID);
|
||||
}
|
||||
return uploadService.getByFileMD5(md5);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分片初始化
|
||||
*
|
||||
* @param fileUploadInfo 文件信息
|
||||
* @return ResponseResult<Object>
|
||||
*/
|
||||
@PostMapping("/multipart/init")
|
||||
public R initMultiPartUpload(@RequestBody FileUploadInfo fileUploadInfo) {
|
||||
log.info("REST: 通过 <{}> 初始化上传任务", fileUploadInfo);
|
||||
return R.ok().setData(uploadService.initMultiPartUpload(fileUploadInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成上传
|
||||
*
|
||||
* @param fileUploadInfo 文件信息
|
||||
* @return ResponseResult<Object>
|
||||
*/
|
||||
@PostMapping("/multipart/merge")
|
||||
public R completeMultiPartUpload(@RequestBody FileUploadInfo fileUploadInfo) {
|
||||
log.info("REST: 通过 {} 合并上传任务", fileUploadInfo);
|
||||
//合并文件
|
||||
String url = uploadService.mergeMultipartUpload(fileUploadInfo);
|
||||
//获取上传文件地址
|
||||
if (!StringUtils.isEmpty(url)) {
|
||||
return R.ok().setData(url);
|
||||
}
|
||||
return R.error();
|
||||
}
|
||||
|
||||
@PostMapping("/multipart/uploadScreenshot")
|
||||
public R uploaduploadScreenshot(@RequestPart("photos") MultipartFile[] photos,
|
||||
@RequestParam("buckName") String buckName) {
|
||||
log.info("REST: 上传文件信息 <{}> ", photos);
|
||||
|
||||
for (MultipartFile photo : photos) {
|
||||
if (!photo.isEmpty()) {
|
||||
uploadService.upload(photo, buckName);
|
||||
}
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping("/createBucket")
|
||||
public void createBucket(@RequestParam("bucketName") String bucketName) {
|
||||
String bucket = minioUtils.createBucket(bucketName);
|
||||
}
|
||||
|
||||
}
|
15
minio-admin/src/main/java/com/mmg/mapper/FilesMapper.java
Normal file
15
minio-admin/src/main/java/com/mmg/mapper/FilesMapper.java
Normal file
@ -0,0 +1,15 @@
|
||||
package com.mmg.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.mmg.model.po.Files;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author LGY
|
||||
*/
|
||||
public interface FilesMapper extends BaseMapper<Files> {
|
||||
|
||||
}
|
65
minio-admin/src/main/java/com/mmg/model/po/Files.java
Normal file
65
minio-admin/src/main/java/com/mmg/model/po/Files.java
Normal file
@ -0,0 +1,65 @@
|
||||
package com.mmg.model.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
*
|
||||
* </p>
|
||||
*
|
||||
* @author LGY
|
||||
*/
|
||||
@Data
|
||||
@TableName("files")
|
||||
public class Files implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
@TableField("upload_id")
|
||||
private String uploadId;
|
||||
|
||||
@TableField("file_md5")
|
||||
private String fileMd5;
|
||||
|
||||
@TableField("url")
|
||||
private String url;
|
||||
|
||||
@TableField("file_name")
|
||||
private String fileName;
|
||||
|
||||
@TableField("bucket_name")
|
||||
private String bucketName;
|
||||
|
||||
@TableField("file_type")
|
||||
private String fileType;
|
||||
|
||||
@TableField("file_size")
|
||||
private Long fileSize;
|
||||
|
||||
@TableField("chunk_size")
|
||||
private Long chunkSize;
|
||||
|
||||
@TableField("chunk_num")
|
||||
private Integer chunkNum;
|
||||
|
||||
@TableField("is_delete")
|
||||
@TableLogic(value = "0",delval = "1")
|
||||
private Boolean isDelete;
|
||||
|
||||
@TableField("enable")
|
||||
private Boolean enable;
|
||||
|
||||
@TableField(value = "create_time", fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.mmg.model.vo;
|
||||
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class FileUploadInfo {
|
||||
|
||||
@NotBlank(message = "文件名不能为空")
|
||||
private String fileName;
|
||||
|
||||
@NotNull(message = "文件大小不能为空")
|
||||
private Long fileSize;
|
||||
|
||||
@NotBlank(message = "Content-Type不能为空")
|
||||
private String contentType;
|
||||
|
||||
@NotNull(message = "分片数量不能为空")
|
||||
private Integer chunkNum;
|
||||
|
||||
@NotBlank(message = "uploadId 不能为空")
|
||||
private String uploadId;
|
||||
|
||||
private Long chunkSize;
|
||||
|
||||
// 桶名称
|
||||
//private String bucketName;
|
||||
|
||||
//md5
|
||||
private String fileMd5;
|
||||
|
||||
//文件类型
|
||||
private String fileType;
|
||||
|
||||
//已上传的分片索引+1
|
||||
private List<Integer> chunkUploadedList;
|
||||
|
||||
}
|
||||
|
50
minio-admin/src/main/java/com/mmg/service/UploadService.java
Normal file
50
minio-admin/src/main/java/com/mmg/service/UploadService.java
Normal file
@ -0,0 +1,50 @@
|
||||
package com.mmg.service;
|
||||
|
||||
|
||||
import com.mmg.model.vo.FileUploadInfo;
|
||||
import com.mmg.utils.R;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import java.util.Map;
|
||||
|
||||
public interface UploadService {
|
||||
/**
|
||||
* 分片上传初始化
|
||||
*
|
||||
* @param fileUploadInfo
|
||||
* @return Map<String, Object>
|
||||
*/
|
||||
Map<String, Object> initMultiPartUpload(FileUploadInfo fileUploadInfo);
|
||||
|
||||
/**
|
||||
* 完成分片上传
|
||||
*
|
||||
* @param fileUploadInfo
|
||||
* @return String
|
||||
*/
|
||||
String mergeMultipartUpload(FileUploadInfo fileUploadInfo);
|
||||
|
||||
/**
|
||||
* 通过 md5 获取已上传的数据
|
||||
* @param md5 String
|
||||
* @return Mono<Map<String, Object>>
|
||||
*/
|
||||
R getByFileMD5(String md5);
|
||||
|
||||
/**
|
||||
* 获取文件地址
|
||||
* @param bucketName
|
||||
* @param fileName
|
||||
*
|
||||
*/
|
||||
String getFliePath(String bucketName, String fileName);
|
||||
|
||||
|
||||
/**
|
||||
* 单文件上传
|
||||
* @param file
|
||||
* @param bucketName
|
||||
* @return
|
||||
*/
|
||||
String upload(MultipartFile file, String bucketName);
|
||||
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
package com.mmg.service.impl;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.mmg.mapper.FilesMapper;
|
||||
import com.mmg.model.po.Files;
|
||||
import com.mmg.model.vo.FileUploadInfo;
|
||||
import com.mmg.service.UploadService;
|
||||
import com.mmg.utils.MinioUtils;
|
||||
import com.mmg.utils.R;
|
||||
import com.mmg.utils.RedisUtil;
|
||||
import com.mmg.utils.RespEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class UploadServiceImpl implements UploadService {
|
||||
|
||||
@Resource
|
||||
private FilesMapper filesMapper;
|
||||
|
||||
@Resource
|
||||
private MinioUtils minioUtils;
|
||||
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Value("${minio.breakpoint-time}")
|
||||
private Integer breakpointTime;
|
||||
|
||||
/**
|
||||
* 通过 md5 获取已上传的数据(断点续传)
|
||||
*
|
||||
* @param md5 String
|
||||
* @return Mono<Map < String, Object>>
|
||||
*/
|
||||
@Override
|
||||
public R getByFileMD5(String md5) {
|
||||
|
||||
log.info("tip message: 通过 <{}> 查询redis是否存在", md5);
|
||||
// 从redis获取文件名称和id
|
||||
FileUploadInfo fileUploadInfo = (FileUploadInfo) redisUtil.get(md5);
|
||||
if (fileUploadInfo != null) {
|
||||
// 正在上传,查询上传后的分片数据
|
||||
List<Integer> chunkList = minioUtils.getChunkByFileMD5(fileUploadInfo.getFileName(), fileUploadInfo.getUploadId(), fileUploadInfo.getFileType());
|
||||
fileUploadInfo.setChunkUploadedList(chunkList);
|
||||
return R.ok(RespEnum.UPLOADING).setData(fileUploadInfo);
|
||||
|
||||
}
|
||||
|
||||
log.info("tip message: 通过 <{}> 查询mysql是否存在", md5);
|
||||
// 查询数据库是否上传成功
|
||||
Files one = filesMapper.selectOne(new LambdaQueryWrapper<Files>().eq(Files::getFileMd5, md5));
|
||||
if (one != null) {
|
||||
FileUploadInfo mysqlsFileUploadInfo = new FileUploadInfo();
|
||||
BeanUtils.copyProperties(one, mysqlsFileUploadInfo);
|
||||
return R.ok(RespEnum.UPLOADSUCCESSFUL).setData(mysqlsFileUploadInfo);
|
||||
}
|
||||
|
||||
return R.ok(RespEnum.NOT_UPLOADED);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 文件分片上传
|
||||
*
|
||||
* @param fileUploadInfo
|
||||
* @return Mono<Map < String, Object>>
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> initMultiPartUpload(FileUploadInfo fileUploadInfo) {
|
||||
|
||||
FileUploadInfo redisFileUploadInfo = (FileUploadInfo) redisUtil.get(fileUploadInfo.getFileMd5());
|
||||
if (redisFileUploadInfo != null) {
|
||||
fileUploadInfo = redisFileUploadInfo;
|
||||
}
|
||||
log.info("tip message: 通过 <{}> 开始初始化<分片上传>任务", fileUploadInfo);
|
||||
// 获取桶
|
||||
String bucketName = minioUtils.getBucketName(fileUploadInfo.getFileType());
|
||||
|
||||
// 单文件上传
|
||||
if (fileUploadInfo.getChunkNum() == 1) {
|
||||
log.info("tip message: 当前分片数量 <{}> 进行单文件上传", fileUploadInfo.getChunkNum());
|
||||
Files files = saveFileToDB(fileUploadInfo);
|
||||
String fileName = files.getUrl().substring(files.getUrl().lastIndexOf("/") + 1);
|
||||
return minioUtils.getUploadObjectUrl(fileName, bucketName);
|
||||
}
|
||||
// 分片上传
|
||||
else {
|
||||
log.info("tip message: 当前分片数量 <{}> 进行分片上传", fileUploadInfo.getChunkNum());
|
||||
Map<String, Object> map = minioUtils.initMultiPartUpload(fileUploadInfo, fileUploadInfo.getFileName(), fileUploadInfo.getChunkNum(), fileUploadInfo.getContentType(), bucketName);
|
||||
String uploadId = (String) map.get("uploadId");
|
||||
fileUploadInfo.setUploadId(uploadId);
|
||||
redisUtil.set(fileUploadInfo.getFileMd5(), fileUploadInfo, breakpointTime * 60 * 60 * 24);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 文件合并
|
||||
*
|
||||
* @param
|
||||
* @return String
|
||||
*/
|
||||
@Override
|
||||
public String mergeMultipartUpload(FileUploadInfo fileUploadInfo) {
|
||||
log.info("tip message: 通过 <{}> 开始合并<分片上传>任务", fileUploadInfo);
|
||||
FileUploadInfo redisFileUploadInfo = (FileUploadInfo) redisUtil.get(fileUploadInfo.getFileMd5());
|
||||
if (redisFileUploadInfo != null) {
|
||||
fileUploadInfo.setFileName(redisFileUploadInfo.getFileName());
|
||||
}
|
||||
boolean result = minioUtils.mergeMultipartUpload(fileUploadInfo.getFileName(), fileUploadInfo.getUploadId(), fileUploadInfo.getFileType());
|
||||
|
||||
//合并成功
|
||||
if (result) {
|
||||
//存入数据库
|
||||
Files files = saveFileToDB(fileUploadInfo);
|
||||
redisUtil.del(fileUploadInfo.getFileMd5());
|
||||
return files.getUrl();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFliePath(String bucketName, String fileName) {
|
||||
return minioUtils.getFliePath(bucketName, fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String upload(MultipartFile file, String bucketName) {
|
||||
minioUtils.upload(file, bucketName);
|
||||
return getFliePath(bucketName, file.getName());
|
||||
}
|
||||
|
||||
private Files saveFileToDB(FileUploadInfo fileUploadInfo) {
|
||||
String suffix = fileUploadInfo.getFileName().substring(fileUploadInfo.getFileName().lastIndexOf("."));
|
||||
String url = this.getFliePath(fileUploadInfo.getFileType().toLowerCase(), fileUploadInfo.getFileMd5() + suffix);
|
||||
//存入数据库
|
||||
Files files = new Files();
|
||||
BeanUtils.copyProperties(fileUploadInfo, files);
|
||||
files.setBucketName(fileUploadInfo.getFileType());
|
||||
files.setUrl(url);
|
||||
filesMapper.insert(files);
|
||||
return files;
|
||||
}
|
||||
}
|
306
minio-admin/src/main/java/com/mmg/utils/MinioUtils.java
Normal file
306
minio-admin/src/main/java/com/mmg/utils/MinioUtils.java
Normal file
@ -0,0 +1,306 @@
|
||||
package com.mmg.utils;
|
||||
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.mmg.config.CustomMinioClient;
|
||||
import com.mmg.model.vo.FileUploadInfo;
|
||||
import io.minio.*;
|
||||
import io.minio.http.Method;
|
||||
import io.minio.messages.Part;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MinioUtils {
|
||||
|
||||
@Value(value = "${minio.endpoint}")
|
||||
private String endpoint;
|
||||
@Value(value = "${minio.accesskey}")
|
||||
private String accesskey;
|
||||
@Value(value = "${minio.secretkey}")
|
||||
private String secretkey;
|
||||
|
||||
@Value(value = "${minio.expiry}")
|
||||
private Integer expiry;
|
||||
|
||||
private CustomMinioClient customMinioClient;
|
||||
|
||||
/**
|
||||
* 用spring的自动注入会注入失败
|
||||
*/
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
MinioClient minioClient = MinioClient.builder()
|
||||
.endpoint(endpoint)
|
||||
.credentials(accesskey, secretkey)
|
||||
.build();
|
||||
customMinioClient = new CustomMinioClient(minioClient);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 单文件签名上传
|
||||
*
|
||||
* @param objectName 文件全路径名称
|
||||
* @param bucketName 桶名称
|
||||
* @return /
|
||||
*/
|
||||
public Map<String, Object> getUploadObjectUrl(String objectName, String bucketName) {
|
||||
try {
|
||||
log.info("tip message: 通过 <{}-{}> 开始单文件上传<minio>", objectName, bucketName);
|
||||
Map<String, Object> resMap = new HashMap();
|
||||
List<String> partList = new ArrayList<>();
|
||||
String url = customMinioClient.getPresignedObjectUrl(
|
||||
GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.PUT)
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.expiry(expiry, TimeUnit.DAYS)
|
||||
.build());
|
||||
log.info("tip message: 单个文件上传、成功");
|
||||
partList.add(url);
|
||||
resMap.put("uploadId", "SingleFileUpload");
|
||||
resMap.put("urlList", partList);
|
||||
return resMap;
|
||||
} catch (Exception e) {
|
||||
log.error("error message: 单个文件上传失败、原因:", e);
|
||||
// 返回 文件上传失败
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化分片上传
|
||||
*
|
||||
* @param fileUploadInfo
|
||||
* @param objectName 文件全路径名称
|
||||
* @param chunkNum 分片数量
|
||||
* @param contentType 类型,如果类型使用默认流会导致无法预览
|
||||
* @param bucketName 桶名称
|
||||
* @return Mono<Map < String, Object>>
|
||||
*/
|
||||
public Map<String, Object> initMultiPartUpload(FileUploadInfo fileUploadInfo, String objectName, int chunkNum, String contentType, String bucketName) {
|
||||
log.info("tip message: 通过 <{}-{}-{}-{}> 开始初始化<分片上传>数据", objectName, chunkNum, contentType, bucketName);
|
||||
Map<String, Object> resMap = new HashMap<>();
|
||||
try {
|
||||
if (CharSequenceUtil.isBlank(contentType)) {
|
||||
contentType = "application/octet-stream";
|
||||
}
|
||||
HashMultimap<String, String> headers = HashMultimap.create();
|
||||
|
||||
headers.put("Content-Type", contentType);
|
||||
|
||||
//获取uploadId
|
||||
String uploadId = null;
|
||||
if (StringUtils.isEmpty(fileUploadInfo.getUploadId())) {
|
||||
uploadId = customMinioClient.initMultiPartUpload(bucketName, null, objectName, headers, null);
|
||||
} else {
|
||||
uploadId = fileUploadInfo.getUploadId();
|
||||
}
|
||||
|
||||
resMap.put("uploadId", uploadId);
|
||||
|
||||
fileUploadInfo.setUploadId(uploadId);
|
||||
fileUploadInfo.setChunkNum(chunkNum);
|
||||
|
||||
List<String> partList = new ArrayList<>();
|
||||
|
||||
Map<String, String> reqParams = new HashMap<>();
|
||||
reqParams.put("uploadId", uploadId);
|
||||
for (int i = 1; i <= chunkNum; i++) {
|
||||
reqParams.put("partNumber", String.valueOf(i));
|
||||
String uploadUrl = customMinioClient.getPresignedObjectUrl(
|
||||
GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.PUT)
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.expiry(1, TimeUnit.DAYS)
|
||||
.extraQueryParams(reqParams)
|
||||
.build());
|
||||
partList.add(uploadUrl);
|
||||
}
|
||||
log.info("tip message: 文件初始化<分片上传>、成功");
|
||||
resMap.put("urlList", partList);
|
||||
return resMap;
|
||||
} catch (Exception e) {
|
||||
log.error("error message: 初始化分片上传失败、原因:", e);
|
||||
// 返回 文件上传失败
|
||||
return R.error(RespEnum.UPLOAD_FILE_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分片上传完后合并
|
||||
*
|
||||
* @param objectName 文件全路径名称
|
||||
* @param uploadId 返回的uploadId
|
||||
* @param bucketName 桶名称
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean mergeMultipartUpload(String objectName, String uploadId, String bucketName) {
|
||||
try {
|
||||
log.info("tip message: 通过 <{}-{}-{}> 合并<分片上传>数据", objectName, uploadId, bucketName);
|
||||
//目前仅做了最大1000分片
|
||||
Part[] parts = new Part[1000];
|
||||
// 查询上传后的分片数据
|
||||
ListPartsResponse partResult = customMinioClient.listMultipart(bucketName, null, objectName, 1000, 0, uploadId, null, null);
|
||||
int partNumber = 1;
|
||||
for (Part part : partResult.result().partList()) {
|
||||
parts[partNumber - 1] = new Part(partNumber, part.etag());
|
||||
partNumber++;
|
||||
}
|
||||
// 合并分片
|
||||
customMinioClient.mergeMultipartUpload(bucketName, null, objectName, uploadId, parts, null, null);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("error message: 合并失败、原因:", e);
|
||||
//TODO删除redis的数据
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 sha256 获取上传中的分片信息
|
||||
*
|
||||
* @param objectName 文件全路径名称
|
||||
* @param uploadId 返回的uploadId
|
||||
* @param bucketName 桶名称
|
||||
* @return Mono<Map < String, Object>>
|
||||
*/
|
||||
public List<Integer> getChunkByFileMD5(String objectName, String uploadId, String bucketName) {
|
||||
log.info("通过 <{}-{}-{}> 查询<minio>上传分片数据", objectName, uploadId, bucketName);
|
||||
try {
|
||||
// 查询上传后的分片数据
|
||||
ListPartsResponse partResult = customMinioClient.listMultipart(bucketName, null, objectName, 1000, 0, uploadId, null, null);
|
||||
return partResult.result().partList().stream().map(Part::partNumber).collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
log.error("error message: 查询上传后的分片信息失败、原因:", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件下载地址
|
||||
*
|
||||
* @param bucketName 桶名称
|
||||
* @param fileName 文件名
|
||||
* @return
|
||||
*/
|
||||
public String getFliePath(String bucketName, String fileName) {
|
||||
return StrUtil.format("{}/{}/{}", endpoint, bucketName, fileName);//文件访问路径
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个桶
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String createBucket(String bucketName) {
|
||||
try {
|
||||
BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucketName).build();
|
||||
//如果桶存在
|
||||
if (customMinioClient.bucketExists(bucketExistsArgs)) {
|
||||
return bucketName;
|
||||
}
|
||||
MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();
|
||||
customMinioClient.makeBucket(makeBucketArgs);
|
||||
return bucketName;
|
||||
} catch (Exception e) {
|
||||
log.error("创建桶失败:{}", e.getMessage());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据文件类型获取minio桶名称
|
||||
*
|
||||
* @param fileType
|
||||
* @return
|
||||
*/
|
||||
public String getBucketName(String fileType) {
|
||||
try {
|
||||
//String bucketName = getProperty(fileType.toLowerCase());
|
||||
|
||||
if (fileType != null && !fileType.equals("")) {
|
||||
//判断桶是否存在
|
||||
String bucketName2 = createBucket(fileType.toLowerCase());
|
||||
if (bucketName2 != null && !bucketName2.equals("")) {
|
||||
return bucketName2;
|
||||
} else {
|
||||
return fileType;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
log.error("Error reading bucket name ");
|
||||
}
|
||||
return fileType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取配置文件
|
||||
*
|
||||
* @param fileType
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private String getProperty(String fileType) throws IOException {
|
||||
Properties SysLocalPropObject = new Properties();
|
||||
//判断桶关系配置文件是否为空
|
||||
if (SysLocalPropObject.isEmpty()) {
|
||||
InputStream is = getClass().getResourceAsStream("/BucketRelation.properties");
|
||||
SysLocalPropObject.load(is);
|
||||
is.close();
|
||||
}
|
||||
return SysLocalPropObject.getProperty("bucket." + fileType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param file 文件
|
||||
* @return Boolean
|
||||
*/
|
||||
public String upload(MultipartFile file, String bucketName) {
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (StringUtils.isEmpty(originalFilename)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
String objectName = file.getName();
|
||||
try {
|
||||
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(objectName)
|
||||
.stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
|
||||
//文件名称相同会覆盖
|
||||
customMinioClient.putObject(objectArgs);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
// 查看文件地址
|
||||
GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(bucketName).object(objectName).method(Method.GET).build();
|
||||
String url = null;
|
||||
try {
|
||||
url = customMinioClient.getPresignedObjectUrl(build);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
82
minio-admin/src/main/java/com/mmg/utils/R.java
Normal file
82
minio-admin/src/main/java/com/mmg/utils/R.java
Normal file
@ -0,0 +1,82 @@
|
||||
package com.mmg.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 返回数据
|
||||
*
|
||||
* @author Mark sunlightcs@gmail.com
|
||||
*/
|
||||
public class R extends HashMap<String, Object> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public R setData(Object data) {
|
||||
put("data",data);
|
||||
return this;
|
||||
}
|
||||
|
||||
//利用fastjson进行反序列化
|
||||
public <T> T getData(Class<T> typeReference) {
|
||||
Object data = get("data"); //默认是map
|
||||
String jsonString = JSON.toJSONString(data);
|
||||
T t = JSON.parseObject(jsonString, typeReference);
|
||||
return t;
|
||||
}
|
||||
|
||||
public R() {
|
||||
put("code", 200);
|
||||
put("msg", "success");
|
||||
}
|
||||
|
||||
public static R error() {
|
||||
return error(5000, "未知异常,请联系管理员");
|
||||
}
|
||||
|
||||
public static R error(String msg) {
|
||||
return error(500, msg);
|
||||
}
|
||||
public static R error(RespEnum respEnum) {
|
||||
return error(respEnum.getCode(), respEnum.getMessage());
|
||||
}
|
||||
public static R error(int code, String msg) {
|
||||
R r = new R();
|
||||
r.put("code", code);
|
||||
r.put("msg", msg);
|
||||
return r;
|
||||
}
|
||||
public static R ok(RespEnum respEnum) {
|
||||
return ok(respEnum.getCode(), respEnum.getMessage());
|
||||
}
|
||||
public static R ok(String msg) {
|
||||
R r = new R();
|
||||
r.put("msg", msg);
|
||||
return r;
|
||||
}
|
||||
public static R ok(int code, String msg) {
|
||||
R r = new R();
|
||||
r.put("code", code);
|
||||
r.put("msg", msg);
|
||||
return r;
|
||||
}
|
||||
public static R ok(Map<String, Object> map) {
|
||||
R r = new R();
|
||||
r.putAll(map);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static R ok() {
|
||||
return new R();
|
||||
}
|
||||
|
||||
public R put(String key, Object value) {
|
||||
super.put(key, value);
|
||||
return this;
|
||||
}
|
||||
public Integer getCode() {
|
||||
|
||||
return (Integer) this.get("code");
|
||||
}
|
||||
|
||||
}
|
509
minio-admin/src/main/java/com/mmg/utils/RedisUtil.java
Normal file
509
minio-admin/src/main/java/com/mmg/utils/RedisUtil.java
Normal file
@ -0,0 +1,509 @@
|
||||
package com.mmg.utils;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class RedisUtil {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate redisTemplate;
|
||||
/**
|
||||
* 指定缓存失效时间
|
||||
* @param key 键
|
||||
* @param time 时间(秒)
|
||||
* @return
|
||||
*/
|
||||
public boolean expire(String key, long time) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.expire(key, time, TimeUnit.SECONDS);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 根据key 获取过期时间
|
||||
* @param key 键 不能为null
|
||||
* @return 时间(秒) 返回0代表为永久有效
|
||||
*/
|
||||
public long getExpire(String key) {
|
||||
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
|
||||
}
|
||||
/**
|
||||
* 判断key是否存在
|
||||
* @param key 键
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public boolean hasKey(String key) {
|
||||
try {
|
||||
return redisTemplate.hasKey(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 删除缓存
|
||||
* @param key 可以传一个值 或多个
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void del(String... key) {
|
||||
if (key != null && key.length > 0) {
|
||||
if (key.length == 1) {
|
||||
redisTemplate.delete(key[0]);
|
||||
} else {
|
||||
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
// ============================String=============================
|
||||
/**
|
||||
* 普通缓存获取
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public Object get(String key) {
|
||||
return key == null ? null : redisTemplate.opsForValue().get(key);
|
||||
}
|
||||
/**
|
||||
* 普通缓存放入
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return true成功 false失败
|
||||
*/
|
||||
public boolean set(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 普通缓存放入并设置时间
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
|
||||
* @return true成功 false 失败
|
||||
*/
|
||||
public boolean set(String key, Object value, long time) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
|
||||
} else {
|
||||
set(key, value);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 递增
|
||||
* @param key 键
|
||||
* @param delta 要增加几(大于0)
|
||||
* @return
|
||||
*/
|
||||
public long incr(String key, long delta) {
|
||||
if (delta < 0) {
|
||||
throw new RuntimeException("递增因子必须大于0");
|
||||
}
|
||||
return redisTemplate.opsForValue().increment(key, delta);
|
||||
}
|
||||
/**
|
||||
* 递减
|
||||
* @param key 键
|
||||
* @param delta 要减少几(小于0)
|
||||
* @return
|
||||
*/
|
||||
public long decr(String key, long delta) {
|
||||
if (delta < 0) {
|
||||
throw new RuntimeException("递减因子必须大于0");
|
||||
}
|
||||
return redisTemplate.opsForValue().increment(key, -delta);
|
||||
}
|
||||
// ================================Map=================================
|
||||
/**
|
||||
* 获取list缓存的长度
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public long hGetMapSize(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForHash().size(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* HashGet
|
||||
* @param key 键 不能为null
|
||||
* @param item 项 不能为null
|
||||
* @return 值
|
||||
*/
|
||||
public Object hget(String key, String item) {
|
||||
return redisTemplate.opsForHash().get(key, item);
|
||||
}
|
||||
/**
|
||||
* 获取hashKey对应的所有键值
|
||||
* @param key 键
|
||||
* @return 对应的多个键值
|
||||
*/
|
||||
public Map<Object, Object> hmget(String key) {
|
||||
return redisTemplate.opsForHash().entries(key);
|
||||
}
|
||||
/**
|
||||
* HashSet
|
||||
* @param key 键
|
||||
* @param map 对应多个键值
|
||||
* @return true 成功 false 失败
|
||||
*/
|
||||
public boolean hmset(String key, Map<String, Object> map) {
|
||||
try {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* HashSet 并设置时间
|
||||
* @param key 键
|
||||
* @param map 对应多个键值
|
||||
* @param time 时间(秒)
|
||||
* @return true成功 false失败
|
||||
*/
|
||||
public boolean hmset(String key, Map<String, Object> map, long time) {
|
||||
try {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 向一张hash表中放入数据,如果不存在将创建
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param value 值
|
||||
* @return true 成功 false失败
|
||||
*/
|
||||
public boolean hset(String key, String item, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForHash().put(key, item, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 向一张hash表中放入数据,如果不存在将创建
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param value 值
|
||||
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
|
||||
* @return true 成功 false失败
|
||||
*/
|
||||
public boolean hset(String key, String item, Object value, long time) {
|
||||
try {
|
||||
redisTemplate.opsForHash().put(key, item, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 删除hash表中的值
|
||||
* @param key 键 不能为null
|
||||
* @param item 项 可以使多个 不能为null
|
||||
*/
|
||||
public void hdel(String key, Object... item) {
|
||||
redisTemplate.opsForHash().delete(key, item);
|
||||
}
|
||||
/**
|
||||
* 判断hash表中是否有该项的值
|
||||
* @param key 键 不能为null
|
||||
* @param item 项 不能为null
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public boolean hHasKey(String key, String item) {
|
||||
return redisTemplate.opsForHash().hasKey(key, item);
|
||||
}
|
||||
/**
|
||||
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param by 要增加几(大于0)
|
||||
* @return
|
||||
*/
|
||||
public double hincr(String key, String item, double by) {
|
||||
return redisTemplate.opsForHash().increment(key, item, by);
|
||||
}
|
||||
/**
|
||||
* hash递减
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param by 要减少记(小于0)
|
||||
* @return
|
||||
*/
|
||||
public double hdecr(String key, String item, double by) {
|
||||
return redisTemplate.opsForHash().increment(key, item, -by);
|
||||
}
|
||||
// ============================set=============================
|
||||
/**
|
||||
* 根据key获取Set中的所有值
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public Set<Object> sGet(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().members(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 根据value从一个set中查询,是否存在
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public boolean sHasKey(String key, Object value) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().isMember(key, value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 将数据放入set缓存
|
||||
* @param key 键
|
||||
* @param values 值 可以是多个
|
||||
* @return 成功个数
|
||||
*/
|
||||
public long sSet(String key, Object... values) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().add(key, values);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 将set数据放入缓存
|
||||
* @param key 键
|
||||
* @param time 时间(秒)
|
||||
* @param values 值 可以是多个
|
||||
* @return 成功个数
|
||||
*/
|
||||
public long sSetAndTime(String key, long time, Object... values) {
|
||||
try {
|
||||
Long count = redisTemplate.opsForSet().add(key, values);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取set缓存的长度
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public long sGetSetSize(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().size(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 移除值为value的
|
||||
* @param key 键
|
||||
* @param values 值 可以是多个
|
||||
* @return 移除的个数
|
||||
*/
|
||||
public long setRemove(String key, Object... values) {
|
||||
try {
|
||||
Long count = redisTemplate.opsForSet().remove(key, values);
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// ===============================list=================================
|
||||
/**
|
||||
* 获取list缓存的内容
|
||||
* @param key 键
|
||||
* @param start 开始
|
||||
* @param end 结束 0 到 -1代表所有值
|
||||
* @return
|
||||
*/
|
||||
public List<Object> lGet(String key, long start, long end) {
|
||||
try {
|
||||
return redisTemplate.opsForList().range(key, start, end);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取list缓存的长度
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public long lGetListSize(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForList().size(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 通过索引 获取list中的值
|
||||
* @param key 键
|
||||
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
|
||||
* @return
|
||||
*/
|
||||
public Object lGetIndex(String key, long index) {
|
||||
try {
|
||||
return redisTemplate.opsForList().index(key, index);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 将list放入缓存
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return
|
||||
*/
|
||||
public boolean lSet(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPush(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 将list放入缓存
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒)
|
||||
* @return
|
||||
*/
|
||||
public boolean lSet(String key, Object value, long time) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPush(key, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 将list放入缓存
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return
|
||||
*/
|
||||
public boolean lSet(String key, List<Object> value) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPushAll(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒)
|
||||
* @return
|
||||
*/
|
||||
public boolean lSet(String key, List<Object> value, long time) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPushAll(key, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 根据索引修改list中的某条数据
|
||||
* @param key 键
|
||||
* @param index 索引
|
||||
* @param value 值
|
||||
* @return
|
||||
*/
|
||||
public boolean lUpdateIndex(String key, long index, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().set(key, index, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 移除N个值为value
|
||||
* @param key 键
|
||||
* @param count 移除多少个
|
||||
* @param value 值
|
||||
* @return 移除的个数
|
||||
*/
|
||||
public long lRemove(String key, long count, Object value) {
|
||||
try {
|
||||
Long remove = redisTemplate.opsForList().remove(key, count, value);
|
||||
return remove;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
28
minio-admin/src/main/java/com/mmg/utils/RespEnum.java
Normal file
28
minio-admin/src/main/java/com/mmg/utils/RespEnum.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.mmg.utils;
|
||||
|
||||
public enum RespEnum {
|
||||
|
||||
UPLOADSUCCESSFUL(1, "上传成功"),
|
||||
UPLOADING(2, "上传中"),
|
||||
NOT_UPLOADED(3, "未上传"),
|
||||
ACCESS_PARAMETER_INVALID(1001,"访问参数无效"),
|
||||
UPLOAD_FILE_FAILED(1002,"文件上传失败"),
|
||||
DATA_NOT_EXISTS(1003,"数据不存在"),
|
||||
;
|
||||
|
||||
private final Integer code;
|
||||
private final String message;
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
RespEnum(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
35
minio-admin/src/main/resources/application.yml
Normal file
35
minio-admin/src/main/resources/application.yml
Normal file
@ -0,0 +1,35 @@
|
||||
server:
|
||||
port: 9090
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/minio_upload_file?serverTimezone=Asia/Shanghai&userUnicode=true&useSSL=false&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: 123456
|
||||
jackson:
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
time-zone: GMT+8
|
||||
data:
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
database: 0
|
||||
#password: 123456
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath:mapper/*.xml
|
||||
configuration:
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
|
||||
minio:
|
||||
endpoint: http://47.103.114.59:9000
|
||||
accesskey: OliveSmart
|
||||
secretkey: OliveSmartTmzl
|
||||
bucket: playedu
|
||||
|
||||
expiry: 1 #分片对象过期时间 单位(天)
|
||||
breakpoint-time: 1 #断点续传有效时间,在redis存储任务的时间 单位(天)
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.mmg: debug
|
||||
org.springframework: warn
|
83
minio-admin/src/main/resources/logback.xml
Normal file
83
minio-admin/src/main/resources/logback.xml
Normal file
@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="60 seconds" debug="false">
|
||||
<!-- 日志存放路径 -->
|
||||
<property name="log.path" value="logs/" />
|
||||
<!-- 日志输出格式 -->
|
||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<!-- 彩色日志 -->
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<pattern>
|
||||
%d{yyyy-MM-dd HH:mm:ss} [%thread] %magenta(%-5level) %green([%-50.50class]) >>> %cyan(%msg) %n
|
||||
</pattern>
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<!-- 系统日志输出 -->
|
||||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/info.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>INFO</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/error.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>ERROR</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 系统模块日志级别控制 -->
|
||||
<logger name="com.zsincere" level="info" />
|
||||
<!-- Spring日志级别控制 -->
|
||||
<logger name="org.springframework" level="warn" />
|
||||
<!-- zsincere-mq 日志级别控制 -->
|
||||
<logger name="com.zsincere.mq.common" level="info" />
|
||||
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
<!--系统操作日志-->
|
||||
<root level="info">
|
||||
<appender-ref ref="file_info" />
|
||||
<appender-ref ref="file_error" />
|
||||
</root>
|
||||
</configuration>
|
@ -0,0 +1,13 @@
|
||||
package com.mmg;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class MinioFileUploadApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
30
minio-fornt/.gitignore
vendored
Normal file
30
minio-fornt/.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
3
minio-fornt/.vscode/extensions.json
vendored
Normal file
3
minio-fornt/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
29
minio-fornt/README.md
Normal file
29
minio-fornt/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
# minio-fornt
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
13
minio-fornt/index.html
Normal file
13
minio-fornt/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
8
minio-fornt/jsconfig.json
Normal file
8
minio-fornt/jsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
2064
minio-fornt/package-lock.json
generated
Normal file
2064
minio-fornt/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
minio-fornt/package.json
Normal file
22
minio-fornt/package.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "minio-fornt",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.2",
|
||||
"element-plus": "^2.7.4",
|
||||
"spark-md5": "^3.0.2",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"vite": "^5.2.8"
|
||||
}
|
||||
}
|
BIN
minio-fornt/public/favicon.ico
Normal file
BIN
minio-fornt/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
10
minio-fornt/src/App.vue
Normal file
10
minio-fornt/src/App.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<script setup>
|
||||
import {RouterLink, RouterView} from 'vue-router'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
49
minio-fornt/src/api/upload.js
Normal file
49
minio-fornt/src/api/upload.js
Normal file
@ -0,0 +1,49 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
//上传信息
|
||||
export function uploadScreenshot(data){
|
||||
return request({
|
||||
url:'upload/multipart/uploadScreenshot',
|
||||
method:'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
//上传信息
|
||||
export function uploadFileInfo(data){
|
||||
return request({
|
||||
url:'upload/multipart/uploadFileInfo',
|
||||
method:'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 上传校验
|
||||
export function checkUpload(MD5) {
|
||||
return request({
|
||||
url: `upload/multipart/check?md5=${MD5}`,
|
||||
method: 'get',
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
// 初始化上传
|
||||
export function initUpload(data) {
|
||||
return request({
|
||||
url: `upload/multipart/init`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
// 初始化上传
|
||||
export function mergeUpload(data) {
|
||||
return request({
|
||||
url: `upload/multipart/merge`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
};
|
||||
|
||||
|
1
minio-fornt/src/assets/logo.svg
Normal file
1
minio-fornt/src/assets/logo.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
After Width: | Height: | Size: 276 B |
7
minio-fornt/src/components/icons/IconCommunity.vue
Normal file
7
minio-fornt/src/components/icons/IconCommunity.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
7
minio-fornt/src/components/icons/IconDocumentation.vue
Normal file
7
minio-fornt/src/components/icons/IconDocumentation.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
|
||||
<path
|
||||
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
7
minio-fornt/src/components/icons/IconEcosystem.vue
Normal file
7
minio-fornt/src/components/icons/IconEcosystem.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
7
minio-fornt/src/components/icons/IconSupport.vue
Normal file
7
minio-fornt/src/components/icons/IconSupport.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
19
minio-fornt/src/components/icons/IconTooling.vue
Normal file
19
minio-fornt/src/components/icons/IconTooling.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
class="iconify iconify--mdi"
|
||||
width="24"
|
||||
height="24"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
13
minio-fornt/src/main.js
Normal file
13
minio-fornt/src/main.js
Normal file
@ -0,0 +1,13 @@
|
||||
import {createApp} from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
|
||||
app.use(router)
|
||||
app.use(ElementPlus)
|
||||
|
||||
app.mount('#app')
|
15
minio-fornt/src/router/index.js
Normal file
15
minio-fornt/src/router/index.js
Normal file
@ -0,0 +1,15 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import FileView from "@/views/FileView.vue";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: FileView
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
109
minio-fornt/src/utils/FileUtil.js
Normal file
109
minio-fornt/src/utils/FileUtil.js
Normal file
@ -0,0 +1,109 @@
|
||||
|
||||
/**
|
||||
* @param: fileName - 文件名称
|
||||
* @param: 数据返回 1) 无后缀匹配 - false
|
||||
* @param: 数据返回 2) 匹配图片 - image
|
||||
* @param: 数据返回 3) 匹配 txt - txt
|
||||
* @param: 数据返回 4) 匹配 excel - excel
|
||||
* @param: 数据返回 5) 匹配 word - word
|
||||
* @param: 数据返回 6) 匹配 pdf - pdf
|
||||
* @param: 数据返回 7) 匹配 ppt - ppt
|
||||
* @param: 数据返回 8) 匹配 视频 - video
|
||||
* @param: 数据返回 9) 匹配 音频 - radio
|
||||
* @param: 数据返回 10) 其他匹配项 - other
|
||||
* @author: ljw
|
||||
**/
|
||||
|
||||
export function fileSuffixTypeUtil(fileName){
|
||||
// 后缀获取
|
||||
var suffix = "";
|
||||
// 获取类型结果
|
||||
var result = "";
|
||||
try {
|
||||
var flieArr = fileName.split(".");
|
||||
suffix = flieArr[flieArr.length - 1];
|
||||
} catch (err) {
|
||||
suffix = "";
|
||||
}
|
||||
// fileName无后缀返回 false
|
||||
if (!suffix) {
|
||||
result = false;
|
||||
return result;
|
||||
}
|
||||
// 图片格式
|
||||
var imglist = ["png", "jpg", "jpeg", "bmp", "gif"];
|
||||
// 进行图片匹配
|
||||
result = imglist.some(function (item) {
|
||||
return item == suffix;
|
||||
});
|
||||
if (result) {
|
||||
result = "image";
|
||||
return result;
|
||||
}
|
||||
// 匹配txt
|
||||
var txtlist = ["txt"];
|
||||
result = txtlist.some(function (item) {
|
||||
return item == suffix;
|
||||
});
|
||||
if (result) {
|
||||
result = "txt";
|
||||
return result;
|
||||
}
|
||||
// 匹配 excel
|
||||
var excelist = ["xls", "xlsx"];
|
||||
result = excelist.some(function (item) {
|
||||
return item == suffix;
|
||||
});
|
||||
if (result) {
|
||||
result = "excel";
|
||||
return result;
|
||||
}
|
||||
// 匹配 word
|
||||
var wordlist = ["doc", "docx"];
|
||||
result = wordlist.some(function (item) {
|
||||
return item == suffix;
|
||||
});
|
||||
if (result) {
|
||||
result = "word";
|
||||
return result;
|
||||
}
|
||||
// 匹配 pdf
|
||||
var pdflist = ["pdf"];
|
||||
result = pdflist.some(function (item) {
|
||||
return item == suffix;
|
||||
});
|
||||
if (result) {
|
||||
result = "pdf";
|
||||
return result;
|
||||
}
|
||||
// 匹配 ppt
|
||||
var pptlist = ["ppt"];
|
||||
result = pptlist.some(function (item) {
|
||||
return item == suffix;
|
||||
});
|
||||
if (result) {
|
||||
result = "ppt";
|
||||
return result;
|
||||
}
|
||||
// 匹配 视频
|
||||
var videolist = ["mp4", "m2v", "mkv","ogg", "flv", "avi", "wmv", "rmvb"];
|
||||
result = videolist.some(function (item) {
|
||||
return item == suffix;
|
||||
});
|
||||
if (result) {
|
||||
result = "video";
|
||||
return result;
|
||||
}
|
||||
// 匹配 音频
|
||||
var radiolist = ["mp3", "wav", "wmv"];
|
||||
result = radiolist.some(function (item) {
|
||||
return item == suffix;
|
||||
});
|
||||
if (result) {
|
||||
result = "radio";
|
||||
return result;
|
||||
}
|
||||
// 其他 文件类型
|
||||
result = "other";
|
||||
return result;
|
||||
};
|
42
minio-fornt/src/utils/request.js
Normal file
42
minio-fornt/src/utils/request.js
Normal file
@ -0,0 +1,42 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const request = axios.create({
|
||||
baseURL: `http://localhost:9090`,
|
||||
timeout: 30000
|
||||
})
|
||||
|
||||
// request 拦截器
|
||||
// 可以自请求发送前对请求做一些处理
|
||||
// 比如统一加token,对请求参数统一加密
|
||||
request.interceptors.request.use(config => {
|
||||
config.headers['Content-Type'] = 'application/json;charset=utf-8';
|
||||
return config
|
||||
}, error => {
|
||||
return Promise.reject(error)
|
||||
});
|
||||
|
||||
// response 拦截器
|
||||
// 可以在接口响应后统一处理结果
|
||||
request.interceptors.response.use(
|
||||
response => {
|
||||
let res = response.data;
|
||||
// 如果是返回的文件
|
||||
if (response.headers === 'blob') {
|
||||
return res
|
||||
}
|
||||
// 兼容服务端返回的字符串数据
|
||||
if (typeof res === 'string') {
|
||||
res = res ? JSON.parse(res) : res
|
||||
console.log(res)
|
||||
}
|
||||
return res;
|
||||
},
|
||||
error => {
|
||||
console.log('err' + error) // for debug
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
export default request
|
||||
|
338
minio-fornt/src/views/FileView.vue
Normal file
338
minio-fornt/src/views/FileView.vue
Normal file
@ -0,0 +1,338 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div style="display:none;">
|
||||
<video width="500" height="240" controls id="upvideo">
|
||||
</video>
|
||||
</div>
|
||||
<h2>上传示例</h2>
|
||||
<div class="upload-demo">
|
||||
<el-upload ref="upload" action="https://jsonplaceholder.typicode.com/posts/"
|
||||
:on-remove="handleRemove" :on-change="handleFileChange" :file-list="data.uploadFileList"
|
||||
:show-file-list="false"
|
||||
:auto-upload="false" multiple>
|
||||
<el-button slot="trigger" type="primary" plain>选择文件</el-button>
|
||||
</el-upload>
|
||||
<el-button style="margin: 5px;" type="success" @click="handler">上传</el-button>
|
||||
<el-button type="danger" @click="clearFileHandler">清空</el-button>
|
||||
</div>
|
||||
<table style="margin-top: 20px">
|
||||
<th>
|
||||
文件名
|
||||
</th>
|
||||
<th>
|
||||
文件大小
|
||||
</th>
|
||||
<th>
|
||||
上传进度
|
||||
</th>
|
||||
<th>
|
||||
状态
|
||||
</th>
|
||||
</table>
|
||||
<!-- 文件列表 -->
|
||||
<div class="file-list-wrapper">
|
||||
<el-collapse>
|
||||
<el-collapse-item v-for="item in data.uploadFileList">
|
||||
<template #title>
|
||||
<div class="upload-file-item">
|
||||
<div class="file-info-item file-name" :title="item.name">{{ item.name }}</div>
|
||||
<div class="file-info-item file-size">{{ item.size }}</div>
|
||||
<div class="file-info-item file-progress">
|
||||
<span class="file-progress-label"></span>
|
||||
<el-progress :percentage="item.uploadProgress" class="file-progress-value"/>
|
||||
</div>
|
||||
<div class="file-info-item file-size"><span></span>
|
||||
<el-tag v-if="item.status === '等待上传'" size="small" type="info">等待上传</el-tag>
|
||||
<el-tag v-else-if="item.status === '校验MD5'" size="small" type="warning">校验MD5</el-tag>
|
||||
<el-tag v-else-if="item.status === '正在上传'" size="small">正在上传</el-tag>
|
||||
<el-tag v-else-if="item.status === '上传成功'" size="small" type="success">上传完成</el-tag>
|
||||
<el-tag v-else size="small">正在上传</el-tag>
|
||||
<!-- <el-tag v-else size="medium" type="danger">上传错误</el-tag>-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="file-chunk-list-wrapper">
|
||||
<!-- 分片列表 -->
|
||||
<el-table :data="item.chunkList" max-height="400" style="width: 100%">
|
||||
<el-table-column prop="chunkNumber" label="分片序号" width="180">
|
||||
</el-table-column>
|
||||
<el-table-column prop="progress" label="上传进度">
|
||||
<template v-slot="{ row }">
|
||||
<el-progress v-if="!row.status || row.progressStatus === 'normal'"
|
||||
:percentage="row.progress"/>
|
||||
<el-progress v-else :percentage="row.progress" :status="row.progressStatus"
|
||||
:text-inside="true" :stroke-width="16"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="180">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, reactive} from 'vue';
|
||||
import {checkUpload, initUpload, mergeUpload, uploadFileInfo} from '@/api/upload';
|
||||
import {fileSuffixTypeUtil} from '@/utils/FileUtil';
|
||||
import axios from 'axios';
|
||||
import SparkMD5 from 'spark-md5';
|
||||
import {ElMessageBox} from "element-plus";
|
||||
|
||||
const FILE_UPLOAD_ID_KEY = 'file_upload_id';
|
||||
const chunkSize = 10 * 1024 * 1024; // 10MB
|
||||
let currentFileIndex = 0;
|
||||
|
||||
const FileStatus = {
|
||||
wait: '等待上传',
|
||||
getMd5: '校验MD5',
|
||||
chip: '正在创建序列',
|
||||
uploading: '正在上传',
|
||||
success: '上传成功',
|
||||
error: '上传错误'
|
||||
};
|
||||
|
||||
const simultaneousUploads = ref(3);
|
||||
const data = reactive({
|
||||
uploadFileList: []
|
||||
});
|
||||
|
||||
const handler = () => {
|
||||
if (data.uploadFileList.length === 0) {
|
||||
ElMessageBox.alert('请先选择文件')
|
||||
return false;
|
||||
}
|
||||
if (currentFileIndex >= data.uploadFileList.length) {
|
||||
ElMessageBox.alert('文件上传完成')
|
||||
return false;
|
||||
}
|
||||
const currentFile = data.uploadFileList[currentFileIndex];
|
||||
currentFile.status = FileStatus.getMd5;
|
||||
currentFile.chunkUploadedList = [];
|
||||
|
||||
getFileMd5(currentFile.raw, async (md5, totalChunks) => {
|
||||
const checkResult = await checkFileUploadedByMd5(md5);
|
||||
if (checkResult.code === 1) {
|
||||
currentFile.status = FileStatus.success;
|
||||
currentFile.uploadProgress = 100;
|
||||
currentFileIndex++;
|
||||
handler();
|
||||
return;
|
||||
} else if (checkResult.code === 2) {
|
||||
currentFile.status = FileStatus.uploading;
|
||||
currentFile.chunkUploadedList = checkResult.data.chunkUploadedList;
|
||||
} else {
|
||||
console.log('未上传');
|
||||
}
|
||||
|
||||
currentFile.status = FileStatus.chip;
|
||||
let fileChunks = createFileChunk(currentFile.raw, chunkSize);
|
||||
let type = fileSuffixTypeUtil(currentFile.name);
|
||||
let param = {
|
||||
fileName: currentFile.name,
|
||||
fileSize: currentFile.size,
|
||||
chunkSize: chunkSize,
|
||||
chunkNum: totalChunks,
|
||||
fileMd5: md5,
|
||||
contentType: 'application/octet-stream',
|
||||
fileType: type,
|
||||
chunkUploadedList: currentFile.chunkUploadedList
|
||||
};
|
||||
|
||||
let uploadIdInfoResult = await getFileUploadUrls(param);
|
||||
let uploadIdInfo = uploadIdInfoResult.data;
|
||||
let uploadUrls = uploadIdInfo.urlList;
|
||||
|
||||
currentFile.chunkList = [];
|
||||
if (uploadUrls && fileChunks.length !== uploadUrls.length) {
|
||||
await ElMessageBox.alert('文件上传完成')
|
||||
return;
|
||||
}
|
||||
|
||||
fileChunks.map((chunkItem, index) => {
|
||||
if (currentFile.chunkUploadedList.indexOf(index + 1) !== -1) {
|
||||
currentFile.chunkList.push({
|
||||
chunkNumber: index + 1,
|
||||
chunk: chunkItem,
|
||||
uploadUrl: uploadUrls[index],
|
||||
progress: 100,
|
||||
progressStatus: 'success',
|
||||
status: '上传成功'
|
||||
});
|
||||
} else {
|
||||
currentFile.chunkList.push({
|
||||
chunkNumber: index + 1,
|
||||
chunk: chunkItem,
|
||||
uploadUrl: uploadUrls[index],
|
||||
progress: 0,
|
||||
status: '—'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let tempFileChunks = [];
|
||||
currentFile.chunkList.forEach((item) => {
|
||||
tempFileChunks.push(item);
|
||||
});
|
||||
|
||||
currentFile.status = FileStatus.uploading;
|
||||
tempFileChunks = processUploadChunkList(tempFileChunks);
|
||||
await uploadChunkBase(tempFileChunks);
|
||||
|
||||
if (uploadIdInfo.uploadId === "SingleFileUpload") {
|
||||
currentFile.status = FileStatus.success;
|
||||
currentFileIndex++;
|
||||
handler();
|
||||
return;
|
||||
} else {
|
||||
const mergeResult = await mergeFile({
|
||||
uploadId: uploadIdInfo.uploadId,
|
||||
fileName: currentFile.name,
|
||||
fileMd5: md5,
|
||||
fileType: type,
|
||||
chunkNum: uploadIdInfo.urlList.length,
|
||||
chunkSize: chunkSize,
|
||||
fileSize: currentFile.size
|
||||
});
|
||||
|
||||
if (!mergeResult.data) {
|
||||
currentFile.status = FileStatus.error;
|
||||
this.$message.error(mergeResult.error);
|
||||
} else {
|
||||
localStorage.removeItem(FILE_UPLOAD_ID_KEY);
|
||||
currentFile.status = FileStatus.success;
|
||||
currentFileIndex++;
|
||||
handler();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const clearFileHandler = () => {
|
||||
data.uploadFileList.splice(0, data.uploadFileList.length);
|
||||
currentFileIndex = 0;
|
||||
};
|
||||
|
||||
const handleFileChange = (file, fileList) => {
|
||||
initFileProperties(file);
|
||||
data.uploadFileList.splice(0, data.uploadFileList.length, ...fileList);
|
||||
console.log("data.uploadFileList", data.uploadFileList)
|
||||
};
|
||||
|
||||
const initFileProperties = (file) => {
|
||||
file.chunkList = [];
|
||||
file.status = FileStatus.wait;
|
||||
file.progressStatus = 'warning';
|
||||
file.uploadProgress = 0;
|
||||
};
|
||||
|
||||
const handleRemove = (file, fileList) => {
|
||||
data.uploadFileList.splice(0, data.uploadFileList.length, ...fileList);
|
||||
};
|
||||
|
||||
const getFileMd5 = (file, callback) => {
|
||||
let fileReader = new FileReader();
|
||||
fileReader.readAsArrayBuffer(file);
|
||||
fileReader.onload = function (e) {
|
||||
let spark = new SparkMD5.ArrayBuffer();
|
||||
spark.append(e.target.result);
|
||||
callback(spark.end(), Math.ceil(file.size / chunkSize));
|
||||
};
|
||||
};
|
||||
|
||||
const createFileChunk = (file, size) => {
|
||||
let fileChunks = [];
|
||||
let cur = 0;
|
||||
while (cur < file.size) {
|
||||
fileChunks.push({file: file.slice(cur, cur + size)});
|
||||
cur += size;
|
||||
}
|
||||
return fileChunks;
|
||||
};
|
||||
|
||||
const checkFileUploadedByMd5 = async (md5) => {
|
||||
const response = await checkUpload(md5);
|
||||
return response;
|
||||
};
|
||||
|
||||
const getFileUploadUrls = async (param) => {
|
||||
const response = await initUpload(param);
|
||||
return response;
|
||||
};
|
||||
|
||||
const processUploadChunkList = (chunkList) => {
|
||||
const temp = [];
|
||||
chunkList.forEach((chunk) => {
|
||||
temp.push(chunk);
|
||||
});
|
||||
return temp;
|
||||
};
|
||||
|
||||
const uploadChunkBase = async (chunkList) => {
|
||||
const uploadPromiseList = [];
|
||||
const limit = simultaneousUploads.value;
|
||||
for (let i = 0; i < chunkList.length; i++) {
|
||||
if (chunkList[i].progress !== 100) {
|
||||
chunkList[i].status = FileStatus.uploading;
|
||||
let params = chunkList[i];
|
||||
uploadPromiseList.push(
|
||||
uploadFileChunk(params)
|
||||
.then(() => {
|
||||
chunkList[i].progress = 100;
|
||||
chunkList[i].progressStatus = 'success';
|
||||
chunkList[i].status = '上传成功';
|
||||
})
|
||||
.catch(() => {
|
||||
chunkList[i].progress = 100;
|
||||
chunkList[i].progressStatus = 'exception';
|
||||
chunkList[i].status = '上传失败';
|
||||
})
|
||||
);
|
||||
}
|
||||
if (uploadPromiseList.length === limit || i === chunkList.length - 1) {
|
||||
await Promise.all(uploadPromiseList);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const uploadFileChunk = async (chunk) => {
|
||||
let formData = new FormData();
|
||||
formData.append('file', chunk.chunk.file);
|
||||
await axios.put(chunk.uploadUrl, formData, {
|
||||
headers: {'Content-Type': 'application/octet-stream'}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.file-list-wrapper {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.upload-file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-info-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
text-align: left;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.file-progress {
|
||||
width: 200px;
|
||||
margin: 0 20px;
|
||||
}
|
||||
</style>
|
16
minio-fornt/vite.config.js
Normal file
16
minio-fornt/vite.config.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
}
|
||||
})
|
42
sql/minio_upload_file.sql
Normal file
42
sql/minio_upload_file.sql
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Navicat Premium Data Transfer
|
||||
|
||||
Source Server : Local
|
||||
Source Server Type : MySQL
|
||||
Source Server Version : 80027 (8.0.27)
|
||||
Source Host : 127.0.0.1:3306
|
||||
Source Schema : minio_upload_file
|
||||
|
||||
Target Server Type : MySQL
|
||||
Target Server Version : 80027 (8.0.27)
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 04/06/2024 13:41:50
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for files
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `files`;
|
||||
CREATE TABLE `files` (
|
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`upload_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '分片上传uploadId',
|
||||
`file_md5` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件md5',
|
||||
`url` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '下载链接',
|
||||
`file_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件名称',
|
||||
`bucket_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '桶名',
|
||||
`file_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件类型',
|
||||
`file_size` bigint NULL DEFAULT NULL COMMENT '文件大小(byte)',
|
||||
`chunk_size` bigint NULL DEFAULT NULL COMMENT '每个分片的大小(byte)',
|
||||
`chunk_num` int NULL DEFAULT NULL COMMENT '分片数量',
|
||||
`is_delete` tinyint(1) NULL DEFAULT 0 COMMENT '是否删除',
|
||||
`enable` tinyint(1) NULL DEFAULT 1 COMMENT '是否禁用链接(0 禁用 1启用)',
|
||||
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 309 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '文件表' ROW_FORMAT = DYNAMIC;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
Loading…
Reference in New Issue
Block a user