1.网站涉及相应文件的上传
①考虑文件的大小
/**
* 校验文件大小是否合法
* 此处文件大小是否合法的临界值是 ONE_MB
* @param multipartFile
* @return
*/
public boolean isSizeLegal(MultipartFile multipartFile){
final long ONE_MB = 1024 * 1024L;
long size = multipartFile.getSize();
if (size > ONE_MB){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"文件过大");
}
return true;
}
②考虑文件是否合法(后缀名是否符合要求)
/**
* 校验后缀名是否合法
* @param multipartFile
* @return
*/
public boolean isSuffixLegal(MultipartFile multipartFile){
String originalFilename = multipartFile.getOriginalFilename();
//调用huTool工具类的getSuffix()获取文件的后缀名
String suffixName = FileUtil.getSuffix(originalFilename);
//新建合法后缀名的白名单
final List<String> validFileSuffixList = Arrays.asList("jpg","png","svg","webp","jpeg","xlsx");
if (!validFileSuffixList.contains(suffixName)){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"文件后缀名不合法");
}
return true;
}
③考虑文件的内容
④考虑文件的合规性(是否包含敏感信息)
可以接入腾讯云的图片万象数据审核
2.数据存储及查询
业务分析:现在有一个图表信息表,里面存储的相关字段如下,如果每一个用户上传的“图表数据”100M(不超过规定的大小),那么该chart表的查询速度则会大大降低
create table chart
(
id bigint auto_increment comment 'id'
primary key,
goal text null comment '分析目标',
chartData text null comment '图表数据',
chartType varchar(128) null comment '图表类型',
genChart text null comment '生成的图表数据',
genResult text null comment '生成的分析结果',
userId bigint null comment '创建用户 id',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
name varchar(128) null comment '图表名称'
)
comment '图表信息表' collate = utf8mb4_unicode_ci;
用户现在只需要查询chart表的“图表数据”进行简单查询,即保存文件的原始数据有什么优化方法?
解决方法:分库分表
把每一个图表对应的原始数据单独保存作为一个新的数据表,而不是都存放在一个字段中
示例:
比如现在要保存 “网站数据.xlsx”,那么我们就新建一个数据表,表名 chart_{图表id},表内字段和“网站数据.xlsx”内字段一一对应并存储相同数据
2.1数据存储
ChartMapper.xml
<insert id="createChartData" parameterType="string">
${InsertSql}
</insert>
ChartMapper接口
声明方法
void createChartData(String insertSql);
测试
@Test
void createChartData() {
String chartId = "1705232019000590337";
String columns_one = "日期";
String columns_two = "人数";
String InsertSql = String.format("create table chart_%s (%s varchar(128),%s
int(10))",chartId,columns_one,columns_two);
chartMapper.createChartData(InsertSql);
//添加数据......
}
结果
新建 chart_1705232019000590337 数据表
2.2数据查询
前提:数据已存储在相应的数据表
ChartMapper.xml
<select id="queryChartData" parameterType="string" resultType="map">
${querySql}
</select>
ChartMapper接口声明对应的方法
List<Map<String,Object>> queryChartData(String querySql);
测试
@Test
void queryChartData() {
String chartID = "1705772545445376001";
String querySql = String.format("select * from chart_%s",chartID);
List<Map<String, Object>> maps = chartMapper.queryChartData(querySql);
System.out.println(maps);
}
结果
对应的数据表存储内容如下
动态sql获取的数据如下
3.限流
关于限流的四种经典算法:面试必备:4种经典限流算法讲解 - 掘金
---使用Redission的RateLimiter
①引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.23.5</version>
</dependency>
②yml配置redis信息---实现动态读取配置
spring:
redis:
database: xxx
host: xxx
port: xxx
timeout: xxx
password:xxx
③新建redis配置类---@Bean注解
@Bean
是用在方法上,将当前方法的返回值对象
放到ioc容器当中!
但是只有方法上有@Bean
是无法将对象放入容器当中的,该@Bean修饰的方法应该在@controller、@Service、@Component、@Configuration、@Repository
修饰的类当中才可以!
@Configuration
@ConfigurationProperties("spring.redis")
注解作用:可以将properties/yml配置文件中的内容读取并封装到JavaBean中(字段名相同)
/**
*redis配置类
*
*/
@Configuration
@ConfigurationProperties("spring.redis")
@Data
public class RedisConfig {
Integer database;
String host;
Integer port;
@Bean
public RedissonClient getRedissonClient(){
// 1. Create config object
Config config = new Config();
config.useSingleServer()
.setDatabase(database)
.setAddress("redis://" + host + ":" + port);
//2.创建实例
RedissonClient redisson = Redisson.create(config);
return redisson;
}
}
④RedisLimiterManager
Manager包---通用的中间组件层
trySetRate()方法的四个参数:
boolean trySetRate(RateType mode, long rate, long rateInterval, RateIntervalUnit rateIntervalUnit); mode – - rate mode 限流类型 rate – - rate 限流速率/速度 rateInterval – - rate time interval 限流的时间间隔 rateIntervalUnit – - rate time interval unit 速率时间间隔单位
tryAcquire():每次请求的令牌数
@Service
public class RedisLimiterManager {
@Resource
private RedissonClient redissonClient;
/**
* 限流器
* @param key 区分不同的限流器,比如用户的id应该分别统计
*/
public void doRateLimiter(String key){
RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
//设置限流器的规则
rateLimiter.trySetRate(RateType.OVERALL,2,1, RateIntervalUnit.SECONDS);
//每当来操作,请求令牌 permits(每次获得许可证的数量)
boolean canOP = rateLimiter.tryAcquire(1);
if (!canOP){
throw new BusinessException(ErrorCode.TOO_MANY_REQUEST,"请求过于频繁");
}
}
}
⑤具体应用
注意:限流器 doRateLimiter(String key)Key参数的设置!
//判断限流
String key = "genChartByAi_" + loginUser.getId();
redisLimiterManager.doRateLimiter(key);
此处的key是具体的方法名+用户的id组合的 字符串