项目完成
This commit is contained in:
commit
d44ffceaab
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
files
|
||||
|
||||
### 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/
|
15
README.md
Normal file
15
README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# 多模态文件管理系统后端工程 filemanage-backend
|
||||
|
||||
## 创建数据库表
|
||||
运行sql/fileManage.sql(基于MySQL,版本不低于5.5,推荐8)
|
||||
|
||||
## 自定义项目配置
|
||||
修改application.yml下的配置信息(或部署后创建生产环境配置文件)
|
||||
|
||||
## 开发环境下启动
|
||||
启动src/main/java/cn/czyx007/filemanage/Application.java
|
||||
|
||||
## 打包工程到生产环境部署
|
||||
1.mvn clean
|
||||
|
||||
2.mvn package
|
116
pom.xml
Normal file
116
pom.xml
Normal file
@ -0,0 +1,116 @@
|
||||
<?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>cn.czyx007</groupId>
|
||||
<artifactId>fileManage</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>fileManage-backend</name>
|
||||
<description>fileManage-backend</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>2.7.6</spring-boot.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.5.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
<version>1.2.21</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.47</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.whvcse</groupId>
|
||||
<artifactId>easy-captcha</artifactId>
|
||||
<version>1.6.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-email</artifactId>
|
||||
<version>1.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.qcloud</groupId>
|
||||
<artifactId>cos_api</artifactId>
|
||||
<version>5.6.205</version>
|
||||
</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>
|
||||
</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>1.8</source>
|
||||
<target>1.8</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<configuration>
|
||||
<mainClass>cn.czyx007.filemanage.Application</mainClass>
|
||||
<skip>false</skip>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>repackage</id>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
53
sql/fileManage.sql
Normal file
53
sql/fileManage.sql
Normal file
@ -0,0 +1,53 @@
|
||||
create database if not exists fileManage charset=utf8mb4;
|
||||
|
||||
use fileManage;
|
||||
|
||||
/**
|
||||
* 文件表
|
||||
*/
|
||||
create table if not exists files(
|
||||
id bigint primary key auto_increment comment '唯一ID',
|
||||
name varchar(255) comment '文件名',
|
||||
store_name varchar(64) comment '文件存储名-uuid',
|
||||
path varchar(255) comment '文件存储路径',
|
||||
size bigint comment '文件大小',
|
||||
type tinyint unsigned comment '文件类型-0:图片 1:视频 2:音频 3:文档 4:其他',
|
||||
create_time datetime comment '文件创建时间',
|
||||
update_time datetime comment '文件更新时间',
|
||||
is_delete boolean default false comment '是否逻辑删除',
|
||||
user_id bigint comment '所属用户ID',
|
||||
oss_id bigint comment '存储策略ID'
|
||||
) DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
/**
|
||||
* 用户表
|
||||
*/
|
||||
create table if not exists user(
|
||||
id bigint primary key auto_increment comment '唯一ID',
|
||||
email varchar(255) comment '邮箱',
|
||||
username varchar(255) comment '用户名',
|
||||
password char(40) comment '密码',
|
||||
create_time datetime comment '注册时间',
|
||||
update_time datetime comment '个人信息更新时间',
|
||||
is_admin boolean default false comment '是否为管理员',
|
||||
storage_used bigint default 0 comment '已使用存储空间(B)',
|
||||
# 默认1024GB
|
||||
storage_total bigint default (1024*1024*1024*1024) comment '总存储空间(B)'
|
||||
)DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
/**
|
||||
* 存储策略表
|
||||
*/
|
||||
create table if not exists oss(
|
||||
id bigint primary key auto_increment comment '唯一ID',
|
||||
name varchar(64) comment '存储策略名',
|
||||
type tinyint unsigned comment '存储策略类型-0:阿里云 1:腾讯云 2:七牛云 3:本地',
|
||||
config json comment '存储策略配置信息',
|
||||
create_time datetime comment '存储策略创建时间',
|
||||
update_time datetime comment '存储策略更新时间',
|
||||
creator bigint comment '存储策略创建者ID',
|
||||
updater bigint comment '存储策略更新者ID',
|
||||
is_active boolean default false comment '是否启用'
|
||||
)DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
insert into oss values (1, 'default', 3, null, now(), now(), -1, -1, true);
|
17
src/main/java/cn/czyx007/filemanage/Application.java
Normal file
17
src/main/java/cn/czyx007/filemanage/Application.java
Normal file
@ -0,0 +1,17 @@
|
||||
package cn.czyx007.filemanage;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableTransactionManagement
|
||||
@EnableCaching
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
74
src/main/java/cn/czyx007/filemanage/bean/Files.java
Normal file
74
src/main/java/cn/czyx007/filemanage/bean/Files.java
Normal file
@ -0,0 +1,74 @@
|
||||
package cn.czyx007.filemanage.bean;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class Files implements Serializable {
|
||||
/**
|
||||
* 唯一ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/*
|
||||
* 文件存储名(UUID形式)
|
||||
*/
|
||||
private String storeName;
|
||||
|
||||
/**
|
||||
* 文件存储路径
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 文件大小
|
||||
*/
|
||||
private Long size;
|
||||
|
||||
/**
|
||||
* 文件类型-0:图片 1:视频 2:音频 3:文档 4:其他
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否删除
|
||||
*/
|
||||
private Integer isDelete;
|
||||
|
||||
/**
|
||||
* 所属用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 存储策略ID
|
||||
*/
|
||||
private String ossId;
|
||||
}
|
66
src/main/java/cn/czyx007/filemanage/bean/OSS.java
Normal file
66
src/main/java/cn/czyx007/filemanage/bean/OSS.java
Normal file
@ -0,0 +1,66 @@
|
||||
package cn.czyx007.filemanage.bean;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@TableName("oss")
|
||||
public class OSS implements Serializable {
|
||||
/**
|
||||
* 唯一ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 存储策略名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 存储策略类型-0:阿里云 1:腾讯云 2:七牛云 3:本地
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 存储策略配置信息
|
||||
*/
|
||||
private String config;
|
||||
|
||||
/**
|
||||
* 存储策略创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 存储策略更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 存储策略创建者ID
|
||||
*/
|
||||
private String creator;
|
||||
|
||||
/**
|
||||
* 存储策略更新者ID
|
||||
*/
|
||||
private String updater;
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private Integer isActive;
|
||||
}
|
65
src/main/java/cn/czyx007/filemanage/bean/User.java
Normal file
65
src/main/java/cn/czyx007/filemanage/bean/User.java
Normal file
@ -0,0 +1,65 @@
|
||||
package cn.czyx007.filemanage.bean;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class User implements Serializable {
|
||||
/**
|
||||
* 唯一ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 注册时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 个人信息更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否为管理员
|
||||
*/
|
||||
private Integer isAdmin;
|
||||
|
||||
/**
|
||||
* 已使用存储空间(B)
|
||||
*/
|
||||
private Long storageUsed;
|
||||
|
||||
/**
|
||||
* 总存储空间(B)
|
||||
* 默认1024GB
|
||||
*/
|
||||
private Long storageTotal;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package cn.czyx007.filemanage.common;
|
||||
|
||||
|
||||
public class CustomException extends RuntimeException{
|
||||
public CustomException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public CustomException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cn.czyx007.filemanage.common;
|
||||
|
||||
import cn.czyx007.filemanage.utils.Result;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
//全局异常处理
|
||||
@RestControllerAdvice(annotations = RestController.class)
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(Exception.class)
|
||||
public Result<String> exceptionHandler(CustomException e){
|
||||
log.error(e.toString());
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package cn.czyx007.filemanage.common;
|
||||
|
||||
import cn.czyx007.filemanage.utils.BaseContext;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class MyMetaObjectHandler implements MetaObjectHandler {
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
log.info("insertFil: {}", metaObject.toString());
|
||||
metaObject.setValue("createTime", LocalDateTime.now());
|
||||
metaObject.setValue("updateTime", LocalDateTime.now());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
log.info("updateFill: {}", metaObject.toString());
|
||||
metaObject.setValue("updateTime", LocalDateTime.now());
|
||||
}
|
||||
}
|
26
src/main/java/cn/czyx007/filemanage/config/CorsConfig.java
Normal file
26
src/main/java/cn/czyx007/filemanage/config/CorsConfig.java
Normal file
@ -0,0 +1,26 @@
|
||||
package cn.czyx007.filemanage.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
@Configuration
|
||||
public class CorsConfig {
|
||||
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
|
||||
config.addAllowedOrigin("http://localhost:8088"); // 允许域名跨域访问
|
||||
config.addAllowedMethod("*"); // 允许所有请求方法跨域访问
|
||||
config.addAllowedHeader("*"); // 允许所有请求头跨域访问
|
||||
config.setAllowCredentials(true);
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cn.czyx007.filemanage.config;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
//MybatisPlus的配置类
|
||||
@Configuration
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
@Bean //该注解加在方法上,表示该方法返回的实例交给spring管理
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor(){
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
|
||||
return interceptor;
|
||||
}
|
||||
}
|
18
src/main/java/cn/czyx007/filemanage/config/WebMvcConfig.java
Normal file
18
src/main/java/cn/czyx007/filemanage/config/WebMvcConfig.java
Normal file
@ -0,0 +1,18 @@
|
||||
package cn.czyx007.filemanage.config;
|
||||
|
||||
import cn.czyx007.filemanage.interceptor.LoginInterceptor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
List<String> list = Arrays.asList("/user/sendVerCode", "/user/captcha", "/user/login", "/user/registry");
|
||||
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns(list);
|
||||
}
|
||||
}
|
@ -0,0 +1,376 @@
|
||||
package cn.czyx007.filemanage.controller;
|
||||
|
||||
import cn.czyx007.filemanage.bean.Files;
|
||||
import cn.czyx007.filemanage.bean.OSS;
|
||||
import cn.czyx007.filemanage.service.FilesService;
|
||||
import cn.czyx007.filemanage.service.OSSService;
|
||||
import cn.czyx007.filemanage.service.UserService;
|
||||
import cn.czyx007.filemanage.utils.BaseContext;
|
||||
import cn.czyx007.filemanage.utils.COSUtil;
|
||||
import cn.czyx007.filemanage.utils.Result;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/file")
|
||||
@Slf4j
|
||||
public class FileController {
|
||||
private static String uploadPath;
|
||||
@Autowired
|
||||
private FilesService filesService;
|
||||
@Autowired
|
||||
private OSSService ossService;
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private StringRedisTemplate redisTemplate;
|
||||
|
||||
@Value("${oss.uploadPath}")
|
||||
public void setUploadPath(String uploadPath) {
|
||||
FileController.uploadPath = uploadPath;
|
||||
}
|
||||
|
||||
@GetMapping("/page/{page}/{pageSize}/{isDelete}")
|
||||
public Result<IPage<Files>> list(@PathVariable("page") Integer page, @PathVariable("pageSize") Integer pageSize,
|
||||
@PathVariable("isDelete") Integer isDelete, @RequestParam("name") String name) {
|
||||
String key = "fileCache::" + BaseContext.getCurrentId() + "_" + page + "_" + pageSize + "_" + isDelete + "_" + name;
|
||||
String res = redisTemplate.opsForValue().get(key);
|
||||
if (res != null){
|
||||
IPage resPage = JSON.parseObject(res, IPage.class);
|
||||
return Result.success(resPage);
|
||||
}
|
||||
|
||||
IPage<Files> iPage = new Page<>(page, pageSize);
|
||||
LambdaQueryWrapper<Files> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.eq(Files::getUserId, BaseContext.getCurrentId()).eq(Files::getIsDelete, isDelete);
|
||||
if(name != null && !name.isEmpty())
|
||||
lqw.like(Files::getName, name);
|
||||
filesService.page(iPage, lqw);
|
||||
|
||||
redisTemplate.opsForValue().set(key, JSON.toJSONString(iPage));
|
||||
return Result.success(iPage);
|
||||
}
|
||||
|
||||
// @GetMapping("/{id}")
|
||||
// public Result<Files> getFile(@PathVariable("id") String id){
|
||||
// Files file = filesService.getFileById(id);
|
||||
// if(file == null)
|
||||
// return Result.error("文件不存在或被删除");
|
||||
// return Result.success(file);
|
||||
// }
|
||||
|
||||
@GetMapping("/preview/{id}")
|
||||
public Result<String> getPreviewFile(@PathVariable("id") String id){
|
||||
String key = "previewFile::"+BaseContext.getCurrentId()+"_"+id;
|
||||
Files file = filesService.getById(id);
|
||||
if(file == null) {
|
||||
redisTemplate.delete(key);
|
||||
return Result.error("文件不存在或被删除");
|
||||
} else {
|
||||
String res = redisTemplate.opsForValue().get(key);
|
||||
if (res != null)
|
||||
return Result.success(res);
|
||||
}
|
||||
|
||||
//云存储,直接返回url
|
||||
if(file.getPath().startsWith("http")) {
|
||||
redisTemplate.opsForValue().set(key, file.getPath());
|
||||
return Result.success(file.getPath());
|
||||
}
|
||||
// 本地存储,读取图片文件内容
|
||||
Path imagePath = Paths.get(file.getPath());
|
||||
byte[] imageBytes;
|
||||
try {
|
||||
imageBytes = java.nio.file.Files.readAllBytes(imagePath);
|
||||
} catch (IOException e) {
|
||||
log.error("读取图片文件内容失败", e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
|
||||
// 将图片的内容转换为 base64 格式
|
||||
String suffix = file.getName().substring(file.getName().lastIndexOf(".")+1);
|
||||
String base64Image = "data:image/"+suffix+";base64,"+Base64.getEncoder().encodeToString(imageBytes);
|
||||
redisTemplate.opsForValue().set(key, base64Image);
|
||||
return Result.success(base64Image);
|
||||
}
|
||||
|
||||
@PostMapping("/upload")
|
||||
public Result<String> upload(@RequestParam("file") MultipartFile multipartFile){
|
||||
if (multipartFile == null || multipartFile.isEmpty()) {
|
||||
return Result.error("未上传文件");
|
||||
}
|
||||
OSS ossConfig = ossService.getActive();
|
||||
if (ossConfig == null) {
|
||||
return Result.error("没有有效的存储策略");
|
||||
}
|
||||
|
||||
//更新用户已用空间
|
||||
if(!userService.updateStorageUsed(multipartFile.getSize(), true))
|
||||
return Result.error("空间不足");
|
||||
|
||||
try {
|
||||
String fileUploadPath = System.getProperty("user.dir") + uploadPath;
|
||||
|
||||
// 处理文件上传逻辑,根据存储策略来保存文件
|
||||
Files file = new Files();
|
||||
|
||||
// 设置文件名
|
||||
file.setName(multipartFile.getOriginalFilename());
|
||||
|
||||
// 设置存储名(UUID 形式,保证唯一性)
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
String originName = multipartFile.getOriginalFilename();
|
||||
String suffix = originName.substring(originName.lastIndexOf("."));
|
||||
String storeName = uuid+suffix;
|
||||
file.setStoreName(storeName);
|
||||
log.info(file.toString());
|
||||
|
||||
// 设置文件存储路径
|
||||
switch (ossConfig.getType()){
|
||||
case 0://阿里云
|
||||
|
||||
break;
|
||||
case 1://腾讯云
|
||||
COSUtil.init(ossConfig);
|
||||
file.setPath(COSUtil.customUrl + BaseContext.getCurrentId() + "/" + storeName);
|
||||
break;
|
||||
case 2://七牛云
|
||||
|
||||
break;
|
||||
case 3://本地
|
||||
String filePath = fileUploadPath + "/" + storeName;
|
||||
file.setPath(filePath);
|
||||
break;
|
||||
}
|
||||
log.info("上传文件保存路径:" + file.getPath());
|
||||
|
||||
// 设置文件大小
|
||||
file.setSize(multipartFile.getSize());
|
||||
|
||||
// 设置文件类型
|
||||
int fileType;
|
||||
String mimeType = java.nio.file.Files.probeContentType(Paths.get(storeName));
|
||||
if (mimeType != null) {
|
||||
if (mimeType.startsWith("image/")) {
|
||||
fileType = 0; // 图片类型
|
||||
} else if (mimeType.startsWith("audio/")) {
|
||||
fileType = 1; // 音频类型
|
||||
} else if (mimeType.startsWith("video/")) {
|
||||
fileType = 2; // 视频类型
|
||||
} else if (mimeType.startsWith("text/") || mimeType.endsWith("pdf") ||
|
||||
mimeType.startsWith("application/vnd.openxmlformats-officedocument") ||
|
||||
mimeType.equals("application/pdf") || mimeType.endsWith("json")) {
|
||||
fileType = 3; // 文档类型
|
||||
} else {
|
||||
fileType = 4; // 其他类型
|
||||
}
|
||||
} else {
|
||||
fileType = 4; // 无法确定类型,设置为其他类型
|
||||
}
|
||||
file.setType(fileType);
|
||||
|
||||
// 设置所属用户ID
|
||||
String userId = BaseContext.getCurrentId();
|
||||
file.setUserId(userId);
|
||||
|
||||
//设置存储策略id
|
||||
file.setOssId(ossConfig.getId());
|
||||
|
||||
// 保存文件到指定目录
|
||||
switch (ossConfig.getType()){
|
||||
case 0://阿里云
|
||||
break;
|
||||
case 1://腾讯云
|
||||
COSUtil.uploadFile(multipartFile, file);
|
||||
break;
|
||||
case 2://七牛云
|
||||
break;
|
||||
case 3://本地
|
||||
File tmpFile = new File(fileUploadPath, storeName);
|
||||
// 检查目录是否存在,如果不存在则创建目录
|
||||
if (!tmpFile.getParentFile().exists()) {
|
||||
tmpFile.getParentFile().mkdirs(); // 创建目录及其父目录
|
||||
}
|
||||
multipartFile.transferTo(tmpFile);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 调用文件服务保存文件信息到数据库
|
||||
filesService.save(file);
|
||||
//清除该用户的文件列表缓存
|
||||
Set keys = redisTemplate.keys("fileCache::" + BaseContext.getCurrentId() + "*");
|
||||
if (keys != null) {
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
|
||||
return Result.success("文件上传成功");
|
||||
} catch (Exception e) {
|
||||
log.error("文件上传失败,", e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/download/{id}")
|
||||
public ResponseEntity<Resource> download(@PathVariable("id") String id) {
|
||||
Files file = filesService.getFileById(id);
|
||||
if (file == null) {
|
||||
return ResponseEntity.status(404).body(null);
|
||||
}
|
||||
// 设置响应头信息
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getName());
|
||||
|
||||
// 创建文件资源对象并返回
|
||||
Resource resource = null;
|
||||
if(file.getPath().startsWith("http")){
|
||||
try {
|
||||
resource = new UrlResource(file.getPath());
|
||||
} catch (MalformedURLException e) {
|
||||
log.error(e.toString());
|
||||
}
|
||||
} else {
|
||||
resource = new FileSystemResource(new File(file.getPath()));
|
||||
}
|
||||
return ResponseEntity.ok().headers(headers).body(resource);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/{isDelete}")
|
||||
public Result<String> updateIsDelete(@PathVariable("id") String id, @PathVariable("isDelete") Integer isDelete){
|
||||
//清除该用户的文件列表缓存
|
||||
Set keys = redisTemplate.keys("fileCache::" + BaseContext.getCurrentId() + "*");
|
||||
if (keys != null) {
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
//清除该用户的该文件预览缓存
|
||||
redisTemplate.delete("previewFile::"+BaseContext.getCurrentId()+"_"+id);
|
||||
|
||||
Files file = filesService.getById(id);
|
||||
if(file == null) {
|
||||
return Result.error("文件不存在或被删除");
|
||||
}
|
||||
if(isDelete == 1){
|
||||
file.setIsDelete(1);
|
||||
filesService.updateById(file);
|
||||
return Result.success("文件删除成功");
|
||||
} else if(isDelete == 0){
|
||||
file.setIsDelete(0);
|
||||
filesService.updateById(file);
|
||||
return Result.success("文件恢复成功");
|
||||
} else return Result.error("参数错误");
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<String> delete(@PathVariable("id") String id){
|
||||
//清除该用户的文件列表缓存
|
||||
Set keys = redisTemplate.keys("fileCache::" + BaseContext.getCurrentId() + "*");
|
||||
if(keys != null)
|
||||
redisTemplate.delete(keys);
|
||||
|
||||
Files file = filesService.getById(id);
|
||||
if(file == null)
|
||||
return Result.error("文件不存在或被删除");
|
||||
if(file.getIsDelete() == 0) {
|
||||
return Result.error("你不能永久删除未在回收站的文件");
|
||||
}
|
||||
//物理删除
|
||||
filesService.removeById(id);
|
||||
if(file.getPath().startsWith("http")){
|
||||
COSUtil.init(ossService.getById(file.getOssId()));
|
||||
COSUtil.createCOSClient().deleteObject(COSUtil.bucketName, BaseContext.getCurrentId()+"/"+file.getStoreName());
|
||||
} else {
|
||||
new File(file.getPath()).delete();
|
||||
}
|
||||
//更新用户已用存储空间
|
||||
userService.updateStorageUsed(file.getSize(), false);
|
||||
|
||||
return Result.success("文件已被彻底删除");
|
||||
}
|
||||
|
||||
@PutMapping("/{isDelete}")
|
||||
public Result<String> batchUpdateIsDelete(@PathVariable("isDelete") Integer isDelete, @RequestBody List<String> ids){
|
||||
//清除该用户的文件列表缓存
|
||||
Set keys = redisTemplate.keys("fileCache::" + BaseContext.getCurrentId() + "*");
|
||||
if (keys != null) {
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
//清除该用户的该文件预览缓存
|
||||
keys = redisTemplate.keys("previewFile::" + BaseContext.getCurrentId() + "*");
|
||||
if (keys != null) {
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
|
||||
List<Files> list = filesService.listByIds(ids);
|
||||
if(list == null) {
|
||||
return Result.error("文件不存在或被删除");
|
||||
}
|
||||
|
||||
if(isDelete == 1){
|
||||
list.forEach(file -> file.setIsDelete(1));
|
||||
filesService.updateBatchById(list);
|
||||
return Result.success("文件批量删除成功");
|
||||
} else if(isDelete == 0){
|
||||
list.forEach(file -> file.setIsDelete(0));
|
||||
filesService.updateBatchById(list);
|
||||
return Result.success("文件批量恢复成功");
|
||||
} else return Result.error("参数错误");
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public Result<String> batchDelete(@RequestBody List<String> ids){
|
||||
//清除该用户的文件列表缓存
|
||||
Set keys = redisTemplate.keys("fileCache::" + BaseContext.getCurrentId() + "*");
|
||||
if(keys != null)
|
||||
redisTemplate.delete(keys);
|
||||
|
||||
List<Files> list = filesService.listByIds(ids);
|
||||
if(list == null)
|
||||
return Result.error("文件不存在或被删除");
|
||||
|
||||
long updateSize = 0;
|
||||
for(Files file : list) {
|
||||
if (file.getIsDelete() == 0) {
|
||||
return Result.error("你不能永久删除未在回收站的文件");
|
||||
}
|
||||
updateSize += file.getSize();
|
||||
}
|
||||
//物理删除
|
||||
filesService.removeBatchByIds(ids);
|
||||
|
||||
for (Files file : list) {
|
||||
if(file.getPath().startsWith("http")){
|
||||
COSUtil.init(ossService.getById(file.getOssId()));
|
||||
COSUtil.createCOSClient().deleteObject(COSUtil.bucketName, BaseContext.getCurrentId()+"/"+file.getStoreName());
|
||||
} else {
|
||||
new File(file.getPath()).delete();
|
||||
}
|
||||
}
|
||||
//更新用户已用存储空间
|
||||
userService.updateStorageUsed(updateSize, false);
|
||||
|
||||
return Result.success("文件已被彻底删除");
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
package cn.czyx007.filemanage.controller;
|
||||
|
||||
import cn.czyx007.filemanage.bean.Files;
|
||||
import cn.czyx007.filemanage.bean.OSS;
|
||||
import cn.czyx007.filemanage.bean.User;
|
||||
import cn.czyx007.filemanage.service.FilesService;
|
||||
import cn.czyx007.filemanage.service.OSSService;
|
||||
import cn.czyx007.filemanage.service.UserService;
|
||||
import cn.czyx007.filemanage.utils.BaseContext;
|
||||
import cn.czyx007.filemanage.utils.Result;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/oss")
|
||||
public class OSSController {
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
@Autowired
|
||||
private OSSService ossService;
|
||||
@Autowired
|
||||
private FilesService filesService;
|
||||
|
||||
@GetMapping
|
||||
public Result<List<OSS>> list(){
|
||||
if(userService.getById(BaseContext.getCurrentId()).getIsAdmin() == 0)
|
||||
return Result.error("无权限");
|
||||
|
||||
List<OSS> list = ossService.list();
|
||||
Map<String, String> idToName = new HashMap<>();
|
||||
|
||||
// 获取所有管理员用户并将其ID与用户名存入idToName映射中
|
||||
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.eq(User::getIsAdmin, 1);
|
||||
List<User> adminUsers = userService.list(lqw);
|
||||
for (User adminUser : adminUsers) {
|
||||
idToName.put(adminUser.getId(), adminUser.getUsername());
|
||||
}
|
||||
|
||||
// 更新OSS对象中的创建者和更新者信息
|
||||
for (OSS oss : list) {
|
||||
String creator = oss.getCreator();
|
||||
if (!"-1".equals(creator)) {
|
||||
String username = idToName.get(creator);
|
||||
if (username != null) {
|
||||
oss.setCreator(username);
|
||||
}
|
||||
}
|
||||
|
||||
String updater = oss.getUpdater();
|
||||
if (!"-1".equals(updater)) {
|
||||
String username = idToName.get(updater);
|
||||
if (username != null) {
|
||||
oss.setUpdater(username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public Result<String> addConfig(@RequestBody Map<String,String> config){
|
||||
if(userService.getById(BaseContext.getCurrentId()).getIsAdmin() == 0)
|
||||
return Result.error("无权限");
|
||||
OSS oss = new OSS();
|
||||
oss.setName(config.get("name"));
|
||||
oss.setType(Integer.valueOf(config.get("type")));
|
||||
|
||||
config.remove("name");
|
||||
config.remove("type");
|
||||
oss.setConfig(JSON.toJSONString(config));
|
||||
|
||||
oss.setCreator(BaseContext.getCurrentId());
|
||||
oss.setUpdater(BaseContext.getCurrentId());
|
||||
ossService.save(oss);
|
||||
return Result.success("存储配置保存成功");
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public Result<String> updateConfig(@RequestBody Map<String, String> config, @PathVariable("id") String id){
|
||||
if(userService.getById(BaseContext.getCurrentId()).getIsAdmin() == 0)
|
||||
return Result.error("无权限");
|
||||
OSS oss = ossService.getById(id);
|
||||
oss.setName(config.get("name"));
|
||||
oss.setUpdater(BaseContext.getCurrentId());
|
||||
|
||||
config.remove("name");
|
||||
config.remove("type");
|
||||
JSONObject storageConfig = JSON.parseObject(oss.getConfig());
|
||||
if(storageConfig != null) {
|
||||
storageConfig.put("customUrl", config.get("customUrl"));
|
||||
oss.setConfig(JSON.toJSONString(storageConfig));
|
||||
} else {
|
||||
oss.setConfig(JSON.toJSONString(config));
|
||||
}
|
||||
|
||||
ossService.updateById(oss);
|
||||
return Result.success("存储配置修改成功");
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/{isActive}")
|
||||
public Result<String> updateConfigStatus(@PathVariable("id") String id, @PathVariable("isActive") Integer isActive){
|
||||
if(userService.getById(BaseContext.getCurrentId()).getIsAdmin() == 0)
|
||||
return Result.error("无权限");
|
||||
OSS oss = ossService.getById(id);
|
||||
if(oss == null)
|
||||
return Result.error("存储配置不存在");
|
||||
|
||||
//启用指定存储策略,将其他策略禁用
|
||||
if(isActive == 0) {
|
||||
oss.setIsActive(1);
|
||||
oss.setUpdater(BaseContext.getCurrentId());
|
||||
ossService.updateById(oss);
|
||||
|
||||
LambdaUpdateWrapper<OSS> luw = new LambdaUpdateWrapper<>();
|
||||
luw.ne(OSS::getId, id).set(OSS::getIsActive, 0);
|
||||
ossService.update(luw);
|
||||
return Result.success("存储策略启用成功");
|
||||
} else {
|
||||
//试图仅仅禁用某一个策略,未知要启用的存储策略
|
||||
//避免误操作导致没有有效的存储策略
|
||||
return Result.error("不允许直接禁用某项存储策略");
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<String> deleteConfig(@PathVariable("id") String id){
|
||||
if(userService.getById(BaseContext.getCurrentId()).getIsAdmin() == 0)
|
||||
return Result.error("无权限");
|
||||
LambdaQueryWrapper<Files> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.eq(Files::getOssId, id);
|
||||
if(filesService.exists(lqw)){
|
||||
return Result.error("该存储策略下存在文件,无法删除");
|
||||
}
|
||||
ossService.removeById(id);
|
||||
return Result.success("存储配置删除成功");
|
||||
}
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
package cn.czyx007.filemanage.controller;
|
||||
|
||||
import cn.czyx007.filemanage.bean.User;
|
||||
import cn.czyx007.filemanage.dto.UserDto;
|
||||
import cn.czyx007.filemanage.service.UserService;
|
||||
import cn.czyx007.filemanage.utils.*;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.wf.captcha.SpecCaptcha;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.mail.EmailException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
@Slf4j
|
||||
public class UserController {
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private StringRedisTemplate redisTemplate;
|
||||
|
||||
//发送注册验证码
|
||||
@PostMapping("/sendVerCode")
|
||||
public Result<String> sendVerCode(@RequestBody Map<String,String> map){
|
||||
//生成6位随机数字验证码
|
||||
String code = ValidateCodeUtils.generateValidateCode(6).toString();
|
||||
log.info("验证码:{}", code);
|
||||
//发送短信,让用户接受验证码
|
||||
try {
|
||||
String email = map.get("email");
|
||||
SendEmailUtils.sendAuthCodeEmail(email, code);
|
||||
//把验证码保存到redis,5分钟有效
|
||||
redisTemplate.opsForValue().set(email + ":code", code, 5, TimeUnit.MINUTES);
|
||||
return Result.success("验证码发送成功,请查看邮箱");
|
||||
} catch (EmailException e) {
|
||||
log.error(e.toString());
|
||||
return Result.error("验证码发送失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/captcha")
|
||||
public Result<String> sendCaptcha() {
|
||||
SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
|
||||
String verCode = specCaptcha.text().toLowerCase();
|
||||
String key = UUID.randomUUID().toString();
|
||||
// 存入redis并设置过期时间为1分钟
|
||||
redisTemplate.opsForValue().set(key, verCode, 1, TimeUnit.MINUTES);
|
||||
// 将key和base64返回给前端
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
map.put("key", key);
|
||||
map.put("image", specCaptcha.toBase64());
|
||||
log.info("验证码:{}, key:{}", specCaptcha.text(), key);
|
||||
return Result.success(JSON.toJSONString(map));
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public Result<String> login(@RequestBody UserDto userDto, HttpServletResponse response){
|
||||
log.info("UserDto: {}", userDto);
|
||||
// 获取redis中的验证码
|
||||
String redisCode = redisTemplate.opsForValue().get(userDto.getVerKey());
|
||||
if(redisCode==null){
|
||||
return Result.error("验证码已过期,请刷新");
|
||||
}
|
||||
// 校验验证码
|
||||
String captcha = userDto.getCaptcha();
|
||||
if (captcha==null || !captcha.toLowerCase().equalsIgnoreCase(redisCode)) {
|
||||
return Result.error("验证码错误");
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.eq(User::getEmail, userDto.getEmail())
|
||||
.eq(User::getPassword, EncryptUtils.hashPassword(userDto.getPassword()))
|
||||
.or(i -> i.eq(User::getUsername, userDto.getEmail())
|
||||
.eq(User::getPassword, EncryptUtils.hashPassword(userDto.getPassword())));
|
||||
|
||||
User user = userService.getOne(lqw);
|
||||
if (userService.getOne(lqw)==null){
|
||||
return Result.error("账号不存在或密码错误");
|
||||
}
|
||||
|
||||
Cookie cookie = new Cookie("user", String.valueOf(user.getId()));
|
||||
cookie.setPath("/");
|
||||
cookie.setDomain("localhost");
|
||||
// cookie.setHttpOnly(true);
|
||||
|
||||
response.addCookie(cookie);
|
||||
BaseContext.setCurrentId(user.getId());
|
||||
log.info("用户登录成功,用户id:{}", user.getId());
|
||||
log.info("cookie:{}",cookie.getValue());
|
||||
|
||||
//验证码使用之后,从redis中删除
|
||||
redisTemplate.delete(userDto.getVerKey());
|
||||
return Result.success("登录成功");
|
||||
}
|
||||
|
||||
@PostMapping("/registry")
|
||||
public Result<String> registry(@RequestBody UserDto user){
|
||||
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.eq(User::getEmail, user.getEmail());
|
||||
if (userService.getOne(lqw)!=null){
|
||||
return Result.error("该邮箱已被注册");
|
||||
}
|
||||
// 获取redis中的验证码
|
||||
String redisCode = redisTemplate.opsForValue().get(user.getEmail()+":code");
|
||||
if(redisCode==null){
|
||||
return Result.error("验证码已过期,请重新获取");
|
||||
}
|
||||
// 校验验证码
|
||||
String verCode = user.getVerificationCode();
|
||||
if (verCode==null || !verCode.equals(redisCode)) {
|
||||
return Result.error("验证码错误");
|
||||
}
|
||||
|
||||
user.setUsername(user.getEmail());
|
||||
user.setPassword(EncryptUtils.hashPassword(user.getPassword()));
|
||||
userService.save(user);
|
||||
log.info("用户注册成功:{}", user);
|
||||
|
||||
//验证码使用之后,从redis中删除
|
||||
redisTemplate.delete(user.getEmail() + ":code");
|
||||
return Result.success("注册成功");
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
public Result<String> logout(HttpServletResponse response){
|
||||
BaseContext.setCurrentId(null);
|
||||
Cookie cookie = new Cookie("user", null);
|
||||
cookie.setPath("/");
|
||||
cookie.setDomain("localhost");
|
||||
// cookie.setHttpOnly(true);
|
||||
cookie.setMaxAge(0);
|
||||
|
||||
response.addCookie(cookie);
|
||||
return Result.success("退出成功");
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Result<User> getUser(){
|
||||
String userId = BaseContext.getCurrentId();
|
||||
log.info("当前用户id:{}", userId);
|
||||
User user = userService.getById(userId);
|
||||
if(user==null){
|
||||
return Result.error("用户不存在");
|
||||
}
|
||||
user.setPassword(null);
|
||||
return Result.success(user);
|
||||
}
|
||||
|
||||
@PutMapping("/updatePassword")
|
||||
public Result<String> updatePassword(@RequestBody Map<String,String> map, HttpServletResponse response){
|
||||
String oldPassword = map.get("oldPassword");
|
||||
String newPassword = map.get("newPassword");
|
||||
User user = userService.getById(BaseContext.getCurrentId());
|
||||
if(user==null){
|
||||
return Result.error("用户不存在");
|
||||
}
|
||||
if(!user.getPassword().equals(EncryptUtils.hashPassword(oldPassword))){
|
||||
return Result.error("旧密码错误");
|
||||
}
|
||||
user.setPassword(EncryptUtils.hashPassword(newPassword));
|
||||
userService.updateById(user);
|
||||
|
||||
//密码修改成功,删除cookie,将用户踢下线
|
||||
BaseContext.setCurrentId(null);
|
||||
Cookie cookie = new Cookie("user", null);
|
||||
cookie.setPath("/");
|
||||
cookie.setDomain("localhost");
|
||||
cookie.setMaxAge(0);
|
||||
response.addCookie(cookie);
|
||||
|
||||
log.info("用户密码修改成功,用户id:{}", user.getId());
|
||||
return Result.success("密码修改成功");
|
||||
}
|
||||
|
||||
@PutMapping("/updateUsername")
|
||||
public Result<String> updateUsername(@RequestBody User user){
|
||||
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.eq(User::getUsername, user.getUsername());
|
||||
if (userService.getOne(lqw)!=null){
|
||||
return Result.error("该用户名已被使用");
|
||||
}
|
||||
user.setId(BaseContext.getCurrentId());
|
||||
userService.updateById(user);
|
||||
log.info("用户修改用户名成功,用户id:{}", user.getId());
|
||||
return Result.success("用户名修改成功");
|
||||
}
|
||||
}
|
40
src/main/java/cn/czyx007/filemanage/dto/UserDto.java
Normal file
40
src/main/java/cn/czyx007/filemanage/dto/UserDto.java
Normal file
@ -0,0 +1,40 @@
|
||||
package cn.czyx007.filemanage.dto;
|
||||
|
||||
import cn.czyx007.filemanage.bean.User;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class UserDto extends User {
|
||||
/**
|
||||
* 确认密码
|
||||
*/
|
||||
private String confirmPassword;
|
||||
|
||||
/**
|
||||
* 邮箱验证码
|
||||
*/
|
||||
private String verificationCode;
|
||||
|
||||
/**
|
||||
* 图片验证码
|
||||
*/
|
||||
private String captcha;
|
||||
|
||||
/**
|
||||
* 图片验证码对应key
|
||||
*/
|
||||
private String verKey;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UserDto{" +
|
||||
"confirmPassword='" + confirmPassword + '\'' +
|
||||
", verificationCode='" + verificationCode + '\'' +
|
||||
", captcha='" + captcha + '\'' +
|
||||
", verKey='" + verKey + '\'' +
|
||||
", " + super.toString() + '\'' +
|
||||
"} ";
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package cn.czyx007.filemanage.interceptor;
|
||||
|
||||
import cn.czyx007.filemanage.utils.BaseContext;
|
||||
import cn.czyx007.filemanage.utils.Result;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Slf4j
|
||||
public class LoginInterceptor implements HandlerInterceptor {
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
//获取cookie['user']
|
||||
String userId = null;
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if ("user".equals(cookie.getName())) {
|
||||
userId = cookie.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("LoginInterceptor >> "+request.getRequestURI()+" >> cookie['user'] = " + userId);
|
||||
//判断是否为空
|
||||
if (userId != null) {
|
||||
log.info("当前用户已经登录,用户id为:{}", userId);
|
||||
BaseContext.setCurrentId(userId);
|
||||
return true;
|
||||
}
|
||||
//未登录,响应数据
|
||||
response.getWriter().write(JSON.toJSONString(Result.error("NOT-LOGIN")));
|
||||
return false;
|
||||
}
|
||||
}
|
12
src/main/java/cn/czyx007/filemanage/mapper/FilesMapper.java
Normal file
12
src/main/java/cn/czyx007/filemanage/mapper/FilesMapper.java
Normal file
@ -0,0 +1,12 @@
|
||||
package cn.czyx007.filemanage.mapper;
|
||||
|
||||
import cn.czyx007.filemanage.bean.Files;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 文件信息(File)表数据库访问层
|
||||
*/
|
||||
@Mapper
|
||||
public interface FilesMapper extends BaseMapper<Files> {
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package cn.czyx007.filemanage.mapper;
|
||||
|
||||
import cn.czyx007.filemanage.bean.OSS;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface OSSMapper extends BaseMapper<OSS> {
|
||||
}
|
15
src/main/java/cn/czyx007/filemanage/mapper/UserMapper.java
Normal file
15
src/main/java/cn/czyx007/filemanage/mapper/UserMapper.java
Normal file
@ -0,0 +1,15 @@
|
||||
package cn.czyx007.filemanage.mapper;
|
||||
|
||||
import cn.czyx007.filemanage.bean.User;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
|
||||
/**
|
||||
* 用户信息(User)表数据库访问层
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserMapper extends BaseMapper<User> {
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package cn.czyx007.filemanage.service;
|
||||
|
||||
import cn.czyx007.filemanage.bean.Files;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* 文件信息(File)表服务接口
|
||||
*/
|
||||
public interface FilesService extends IService<Files> {
|
||||
/**
|
||||
* 查询id对应的未被删除的文件
|
||||
* @param id 文件唯一id
|
||||
* @return 文件对象Files
|
||||
*/
|
||||
Files getFileById(String id);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package cn.czyx007.filemanage.service;
|
||||
|
||||
import cn.czyx007.filemanage.bean.OSS;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
public interface OSSService extends IService<OSS> {
|
||||
OSS getActive();
|
||||
}
|
17
src/main/java/cn/czyx007/filemanage/service/UserService.java
Normal file
17
src/main/java/cn/czyx007/filemanage/service/UserService.java
Normal file
@ -0,0 +1,17 @@
|
||||
package cn.czyx007.filemanage.service;
|
||||
|
||||
import cn.czyx007.filemanage.bean.User;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* 用户信息(User)表服务接口
|
||||
*/
|
||||
public interface UserService extends IService<User> {
|
||||
/**
|
||||
* 更新用户已使用存储空间
|
||||
* @param updateSize 变化的存储空间大小
|
||||
* @param isAdd 是否增加
|
||||
* @return true:更新成功;false:更新失败(用户剩余存储空间不足)
|
||||
*/
|
||||
boolean updateStorageUsed(long updateSize, boolean isAdd);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package cn.czyx007.filemanage.service.impl;
|
||||
|
||||
import cn.czyx007.filemanage.bean.Files;
|
||||
import cn.czyx007.filemanage.mapper.FilesMapper;
|
||||
import cn.czyx007.filemanage.service.FilesService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 文件信息(File)表服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class FilesServiceImpl extends ServiceImpl<FilesMapper, Files> implements FilesService {
|
||||
@Override
|
||||
public Files getFileById(String id){
|
||||
LambdaQueryWrapper<Files> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.eq(Files::getId, id).eq(Files::getIsDelete, 0);
|
||||
return this.getOne(lqw);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package cn.czyx007.filemanage.service.impl;
|
||||
|
||||
import cn.czyx007.filemanage.bean.OSS;
|
||||
import cn.czyx007.filemanage.mapper.OSSMapper;
|
||||
import cn.czyx007.filemanage.service.OSSService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class OSSServiceImpl extends ServiceImpl<OSSMapper, OSS> implements OSSService {
|
||||
@Override
|
||||
public OSS getActive(){
|
||||
return getOne(new LambdaQueryWrapper<OSS>().eq(OSS::getIsActive, true));
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package cn.czyx007.filemanage.service.impl;
|
||||
|
||||
import cn.czyx007.filemanage.bean.User;
|
||||
import cn.czyx007.filemanage.mapper.UserMapper;
|
||||
import cn.czyx007.filemanage.service.UserService;
|
||||
import cn.czyx007.filemanage.utils.BaseContext;
|
||||
import cn.czyx007.filemanage.utils.Result;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 用户信息(User)表服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
|
||||
@Override
|
||||
public boolean updateStorageUsed(long updateSize, boolean isAdd) {
|
||||
User user = getById(BaseContext.getCurrentId());
|
||||
if(isAdd) {
|
||||
long newSize = updateSize + user.getStorageUsed();
|
||||
//检查是否有足够空间用于存储
|
||||
if(newSize > user.getStorageTotal())
|
||||
return false;
|
||||
user.setStorageUsed(newSize);
|
||||
}
|
||||
else
|
||||
user.setStorageUsed(user.getStorageUsed() - updateSize);
|
||||
updateById(user);
|
||||
return true;
|
||||
}
|
||||
}
|
22
src/main/java/cn/czyx007/filemanage/utils/BaseContext.java
Normal file
22
src/main/java/cn/czyx007/filemanage/utils/BaseContext.java
Normal file
@ -0,0 +1,22 @@
|
||||
package cn.czyx007.filemanage.utils;
|
||||
|
||||
/**
|
||||
* 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id
|
||||
*/
|
||||
public class BaseContext {
|
||||
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
|
||||
/**
|
||||
* 设置值
|
||||
* @param id
|
||||
*/
|
||||
public static void setCurrentId(String id){
|
||||
threadLocal.set(id);
|
||||
}
|
||||
/**
|
||||
* 获取值
|
||||
* @return
|
||||
*/
|
||||
public static String getCurrentId(){
|
||||
return threadLocal.get();
|
||||
}
|
||||
}
|
121
src/main/java/cn/czyx007/filemanage/utils/COSUtil.java
Normal file
121
src/main/java/cn/czyx007/filemanage/utils/COSUtil.java
Normal file
@ -0,0 +1,121 @@
|
||||
package cn.czyx007.filemanage.utils;
|
||||
|
||||
import cn.czyx007.filemanage.bean.Files;
|
||||
import cn.czyx007.filemanage.bean.OSS;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.qcloud.cos.COSClient;
|
||||
import com.qcloud.cos.ClientConfig;
|
||||
import com.qcloud.cos.auth.BasicCOSCredentials;
|
||||
import com.qcloud.cos.auth.COSCredentials;
|
||||
import com.qcloud.cos.http.HttpProtocol;
|
||||
import com.qcloud.cos.model.*;
|
||||
import com.qcloud.cos.region.Region;
|
||||
import com.qcloud.cos.transfer.TransferManager;
|
||||
import com.qcloud.cos.transfer.TransferManagerConfiguration;
|
||||
import com.qcloud.cos.transfer.Upload;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Slf4j
|
||||
public class COSUtil {
|
||||
// private static COSClient cosClient = null;
|
||||
// private static TransferManager transferManager = null;
|
||||
private static String secretId;
|
||||
private static String secretKey;
|
||||
public static String regionName;
|
||||
public static String bucketName;
|
||||
public static String customUrl;
|
||||
|
||||
public static void init(OSS oss) {
|
||||
JSONObject config = JSON.parseObject(oss.getConfig());
|
||||
secretId = (String) config.get("secretId");
|
||||
secretKey = (String) config.get("secretKey");
|
||||
regionName = (String) config.get("regionName");
|
||||
bucketName = (String) config.get("bucketName");
|
||||
if(config.get("customUrl") == null)
|
||||
customUrl = "https://"+COSUtil.bucketName + ".cos." + COSUtil.regionName + ".myqcloud.com/";
|
||||
else customUrl = "https://"+config.get("customUrl")+"/";
|
||||
}
|
||||
|
||||
// 创建 COSClient 实例,这个实例用来后续调用请求
|
||||
public static COSClient createCOSClient() {
|
||||
// 设置用户身份信息。
|
||||
COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
|
||||
// ClientConfig 中包含了后续请求 COS 的客户端设置:
|
||||
ClientConfig clientConfig = new ClientConfig();
|
||||
|
||||
// 设置 bucket 的地域
|
||||
// COS_REGION 请参见 https://cloud.tencent.com/document/product/436/6224
|
||||
clientConfig.setRegion(new Region(regionName));
|
||||
|
||||
// 设置请求协议, http 或者 https
|
||||
// 5.6.53 及更低的版本,建议设置使用 https 协议
|
||||
// 5.6.54 及更高版本,默认使用了 https
|
||||
clientConfig.setHttpProtocol(HttpProtocol.https);
|
||||
|
||||
// 生成 cos 客户端。
|
||||
return new COSClient(cred, clientConfig);
|
||||
}
|
||||
|
||||
// 创建 TransferManager 实例,这个实例用来后续调用高级接口
|
||||
public static TransferManager createTransferManager() {
|
||||
// 创建一个 COSClient 实例,这是访问 COS 服务的基础实例。
|
||||
// 详细代码参见本页: 简单操作 -> 创建 COSClient
|
||||
COSClient cosClient = createCOSClient();
|
||||
|
||||
// 自定义线程池大小,建议在客户端与 COS 网络充足(例如使用腾讯云的 CVM,同地域上传 COS)的情况下,设置成16或32即可,可较充分的利用网络资源
|
||||
// 对于使用公网传输且网络带宽质量不高的情况,建议减小该值,避免因网速过慢,造成请求超时。
|
||||
ExecutorService threadPool = Executors.newFixedThreadPool(16);
|
||||
|
||||
// 传入一个 threadpool, 若不传入线程池,默认 TransferManager 中会生成一个单线程的线程池。
|
||||
TransferManager transferManager = new TransferManager(cosClient, threadPool);
|
||||
|
||||
// 设置高级接口的配置项
|
||||
// 分块上传阈值和分块大小分别为 5MB 和 1MB
|
||||
TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
|
||||
transferManagerConfiguration.setMultipartUploadThreshold(5*1024*1024);
|
||||
transferManagerConfiguration.setMinimumUploadPartSize(1*1024*1024);
|
||||
transferManager.setConfiguration(transferManagerConfiguration);
|
||||
|
||||
return transferManager;
|
||||
}
|
||||
|
||||
public static void uploadFile(MultipartFile multipartFile, Files file) throws Exception {
|
||||
// 使用高级接口必须先保证本进程存在一个 TransferManager 实例,如果没有则创建
|
||||
// 详细代码参见本页:高级接口 -> 创建 TransferManager
|
||||
TransferManager transferManager = createTransferManager();
|
||||
|
||||
ObjectMetadata objectMetadata = new ObjectMetadata();
|
||||
// 上传的流如果能够获取准确的流长度,则推荐一定填写 content-length
|
||||
// 如果确实没办法获取到,则下面这行可以省略,但同时高级接口也没办法使用分块上传了
|
||||
objectMetadata.setContentLength(multipartFile.getSize());
|
||||
|
||||
// 对象键(Key)是对象在存储桶中的唯一标识。
|
||||
String key = BaseContext.getCurrentId()+"/"+file.getStoreName();
|
||||
|
||||
try(InputStream inputStream = multipartFile.getInputStream()) {
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, inputStream, objectMetadata);
|
||||
|
||||
// 设置存储类型(如有需要,不需要请忽略此行代码), 默认是标准(Standard), 低频(standard_ia)
|
||||
// 更多存储类型请参见 https://cloud.tencent.com/document/product/436/33417
|
||||
putObjectRequest.setStorageClass(StorageClass.Standard);
|
||||
// 高级接口会返回一个异步结果Upload
|
||||
// 可同步地调用 waitForUploadResult 方法等待上传完成,成功返回 UploadResult, 失败抛出异常
|
||||
Upload upload = transferManager.upload(putObjectRequest);
|
||||
upload.waitForUploadResult();
|
||||
}
|
||||
shutdownTransferManager(transferManager);
|
||||
}
|
||||
|
||||
public static void shutdownTransferManager(TransferManager transferManager) {
|
||||
if(transferManager != null) {
|
||||
// 指定参数为 false, 则不会关闭 transferManager 内部的 COSClient 实例。
|
||||
transferManager.shutdownNow(true);
|
||||
}
|
||||
}
|
||||
}
|
31
src/main/java/cn/czyx007/filemanage/utils/EncryptUtils.java
Normal file
31
src/main/java/cn/czyx007/filemanage/utils/EncryptUtils.java
Normal file
@ -0,0 +1,31 @@
|
||||
package cn.czyx007.filemanage.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
@Slf4j
|
||||
public class EncryptUtils {
|
||||
public static String hashPassword(String password) {
|
||||
try {
|
||||
// 创建 MessageDigest 实例并指定算法为 SHA-1
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
// 将密码转换为字节数组
|
||||
byte[] passwordBytes = password.getBytes();
|
||||
// 使用 MessageDigest 更新字节数组
|
||||
byte[] hashedBytes = md.digest(passwordBytes);
|
||||
|
||||
// 将字节数组转换为十六进制字符串
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : hashedBytes) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// 处理算法不存在异常
|
||||
log.error("密码加密失败:", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
36
src/main/java/cn/czyx007/filemanage/utils/Result.java
Normal file
36
src/main/java/cn/czyx007/filemanage/utils/Result.java
Normal file
@ -0,0 +1,36 @@
|
||||
package cn.czyx007.filemanage.utils;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 通用返回结果,服务端响应的数据最终都会封装成此对象
|
||||
* @param <T>
|
||||
*/
|
||||
@Data
|
||||
public class Result<T> implements Serializable {
|
||||
private Integer code; //编码:1成功,0和其它数字为失败
|
||||
private String msg; //错误信息
|
||||
private T data; //数据
|
||||
private Map map = new HashMap(); //动态数据
|
||||
|
||||
public static <T> Result<T> success(T object) {
|
||||
Result<T> result = new Result<T>();
|
||||
result.data = object;
|
||||
result.code = 1;
|
||||
return result;
|
||||
}
|
||||
public static <T> Result<T> error(String msg) {
|
||||
Result result = new Result();
|
||||
result.msg = msg;
|
||||
result.code = 0;
|
||||
return result;
|
||||
}
|
||||
public Result<T> add(String key, Object value) {
|
||||
this.map.put(key, value);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package cn.czyx007.filemanage.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.mail.EmailException;
|
||||
import org.apache.commons.mail.HtmlEmail;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author : 张宇轩
|
||||
* @createTime : 2023/1/19 - 15:43
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SendEmailUtils {
|
||||
private static String hostName;
|
||||
private static String userName;
|
||||
private static String password;
|
||||
|
||||
@Value("${email.hostName}")
|
||||
public void setHostName(String hostName) {
|
||||
SendEmailUtils.hostName = hostName;
|
||||
}
|
||||
|
||||
@Value("${email.userName}")
|
||||
public void setUserName(String userName) {
|
||||
SendEmailUtils.userName = userName;
|
||||
}
|
||||
|
||||
@Value("${email.password}")
|
||||
public void setPassword(String password) {
|
||||
SendEmailUtils.password = password;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
* @param email 接收邮箱
|
||||
* @param code 验证码
|
||||
*/
|
||||
public static void sendAuthCodeEmail(String email,String code) throws EmailException {
|
||||
HtmlEmail mail = new HtmlEmail();
|
||||
/*发送邮件的服务器 126邮箱为smtp.126.com,163邮箱为163.smtp.com,QQ为smtp.qq.com*/
|
||||
mail.setHostName(hostName);
|
||||
mail.setSmtpPort(465);
|
||||
|
||||
// 使用 SSL 连接
|
||||
mail.setSSLOnConnect(true);
|
||||
|
||||
/*不设置发送的消息有可能是乱码*/
|
||||
mail.setCharset("UTF-8");
|
||||
/*IMAP/SMTP服务的密码 username为你开启发送验证码功能的邮箱号 password为你在qq邮箱获取到的一串字符串*/
|
||||
mail.setAuthentication(userName, password);
|
||||
/*发送邮件的邮箱和发件人*/
|
||||
mail.setFrom(userName, "文件管理系统");
|
||||
/*使用安全链接*/
|
||||
mail.setSSLOnConnect(true);
|
||||
/*接收的邮箱*/
|
||||
mail.addTo(email);
|
||||
/*设置邮件的主题*/
|
||||
mail.setSubject("登录验证码");
|
||||
/*设置邮件的内容*/
|
||||
mail.setMsg("尊敬的用户:你好! 登录验证码为:" + code + "(有效期为5分钟)");
|
||||
mail.send();//发送
|
||||
log.info("邮件发送成功");
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package cn.czyx007.filemanage.utils;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 随机生成验证码工具类
|
||||
*/
|
||||
public class ValidateCodeUtils {
|
||||
/**
|
||||
* 随机生成验证码
|
||||
* @param length 长度为4位或者6位
|
||||
* @return
|
||||
*/
|
||||
public static Integer generateValidateCode(int length){
|
||||
Integer code =null;
|
||||
if(length == 4){
|
||||
code = new Random().nextInt(9999);//生成随机数,最大为9999
|
||||
if(code < 1000){
|
||||
code = code + 1000;//保证随机数为4位数字
|
||||
}
|
||||
}else if(length == 6){
|
||||
code = new Random().nextInt(999999);//生成随机数,最大为999999
|
||||
if(code < 100000){
|
||||
code = code + 100000;//保证随机数为6位数字
|
||||
}
|
||||
}else{
|
||||
throw new RuntimeException("只能生成4位或6位数字验证码");
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机生成指定长度字符串验证码
|
||||
* @param length 长度
|
||||
* @return
|
||||
*/
|
||||
public static String generateValidateCode4String(int length){
|
||||
Random rdm = new Random();
|
||||
String hash1 = Integer.toHexString(rdm.nextInt());
|
||||
String capstr = hash1.substring(0, length);
|
||||
return capstr;
|
||||
}
|
||||
}
|
55
src/main/resources/application.yml
Normal file
55
src/main/resources/application.yml
Normal file
@ -0,0 +1,55 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath*:/mapper/**/*.xml
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: ASSIGN_ID
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
|
||||
oss:
|
||||
uploadPath: '/files'
|
||||
|
||||
#用于发送邮箱验证码的账户和密码
|
||||
email:
|
||||
#发送邮件的服务器域名
|
||||
#126邮箱为smtp.126.com,163邮箱为163.smtp.com,QQ个人邮箱为smtp.qq.com,腾讯企业邮为smtp.exmail.qq.com
|
||||
hostName: smtp.qq.com
|
||||
userName: xxx@qq.com
|
||||
password: xxx
|
||||
|
||||
spring:
|
||||
jackson:
|
||||
serialization:
|
||||
fail-on-empty-beans: false
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: -1
|
||||
max-request-size: -1
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
database: 0
|
||||
datasource:
|
||||
druid:
|
||||
#MySQL
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/fileManage?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
|
||||
username: xxx
|
||||
password: xxx
|
||||
#初始化连接数
|
||||
initial-size: 1
|
||||
#最小空闲连接
|
||||
min-idle: 1
|
||||
#最大活动连接
|
||||
max-active: 20
|
||||
#获取连接时测试是否可用
|
||||
test-on-borrow: true
|
||||
#监控页面启动
|
||||
filter:
|
||||
wall:
|
||||
config:
|
||||
start-transaction-allow: true
|
21
src/main/resources/logback-spring.xml
Normal file
21
src/main/resources/logback-spring.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<include resource="org/springframework/boot/logging/logback/base.xml" />
|
||||
<logger name="org.springframework.web" level="INFO"/>
|
||||
<logger name="org.springboot.sample" level="TRACE" />
|
||||
|
||||
<!-- 开发、测试环境 -->
|
||||
<springProfile name="dev,test">
|
||||
<logger name="org.springframework.web" level="INFO"/>
|
||||
<logger name="org.springboot.sample" level="INFO" />
|
||||
<logger name="cn.czyx007" level="DEBUG" />
|
||||
</springProfile>
|
||||
|
||||
<!-- 生产环境 -->
|
||||
<springProfile name="prod">
|
||||
<logger name="org.springframework.web" level="ERROR"/>
|
||||
<logger name="org.springboot.sample" level="ERROR" />
|
||||
<logger name="cn.czyx007" level="ERROR" />
|
||||
</springProfile>
|
||||
|
||||
</configuration>
|
12
src/test/java/cn/czyx007/filemanage/ApplicationTests.java
Normal file
12
src/test/java/cn/czyx007/filemanage/ApplicationTests.java
Normal file
@ -0,0 +1,12 @@
|
||||
package cn.czyx007.filemanage;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class ApplicationTests {
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user