目录
一、总体思路
二、获取文件大小的方式
三、BigDecimal
四、具体实现代码
mysql创建文件表
上传文件的代码
从库中查询当前用户文件总size代码
删除文件的代码
一、总体思路
首先有一张文件记录表,记录每个上传到云端的文件对应的用户id、文件大小(单位为B)、文件url等信息 ;
文件的总size根据表内该用户的所有文件记录的file_size字段累加得到,将这个值存入redis;
每次上传文件的时候,先获取本次文件的size,再用redisson进行加锁,去redis中取到该用户所有文件总size,再设定的值进行比较,如果小于设定值则正常插入数据,大于则抛出异常
二、获取文件大小的方式
一般和前后端进行文件交互,都是通过MultipartFile实现的,它有一个方法:getSize()
无需参数,返回的是该文件Long类型的字节。
我们可以通过它获取到上传的文件的原字节
我本地有一个文件,可以看到它的文件大小,一会我就用它进行测试:
然和测试接口,就用这个文件:
可以发现,获取到的文件大小和原字节是一模一样的
虽然可以获取到文件的原字节大小,但是有的需求是需要我们把这个字节进行单位转换的。
这时候需要我们一个方法,把获取到的文件字节传进去,然后返回的就是字节转换后的文件大小
private String formatSize(long fileS) {
DecimalFormat df = new DecimalFormat("#.00");
String fileSizeString = "";
String wrongSize = "0B";
if (fileS == 0) {
return wrongSize;
}
if (fileS < 1024) {
fileSizeString = df.format((double) fileS) + "B";
} else if (fileS < 1048576) {
fileSizeString = df.format((double) fileS / 1024) + "KB";
} else if (fileS < 1073741824) {
fileSizeString = df.format((double) fileS / 1048576) + "MB";
} else {
fileSizeString = df.format((double) fileS / 1073741824) + "GB";
}
return fileSizeString;
}
三、BigDecimal
为了不损失精度,我们在Java中用BigDecimal来进行相关的计算
1,decimal在java中的用BigDecimal表示
@Column(length=10 ,scale=2) // length表示长度 , scale表示小数点后位数
private BigDecimal money;
2,BigDecimal类型与string,Double类型的相互转换
1. String类型转成BigDecimal类型:
BigDecimal bd = new BigDecimal("xxx");
String str = bd.toString(); 2.BigDecimal类型转成String类型:
BigDecimal bd = new BigDecimal("xxx");
String str = bd.toString(); 3.将double类型转化为BigDecimal
Double a=1.23;
BigDecimal c = BigDecimal.valueOf(a);
不能使用:
BigDecimal decimal= new BigDecimal(a); 4.将BigDecimal类型转化为double
BigDecimal bd;
double d = bd.doubleValue();
3,BigDecimal操作运算加减乘除
BigDecimal num1 = new BigDecimal("100");
BigDecimal num2 = new BigDecimal("50");
//加法
BigDecimal result1 = num1.add(num2);
//减法
BigDecimal result2 = num1.subtract(num2);
//乘法
BigDecimal result3 = num1.multiply(num2);
//除法
BigDecimal result4 = num1.divide(num2);
4,BigDecimal之间的比较
使用compareTo方法
int flag = bigdemical1.compareTo(bigdemical2)
flag = -1,表示bigdemical1小于bigdemical2;
flag = 0,表示bigdemical1等于bigdemical2;
flag = 1,表示bigdemical1大于bigdemical2;
四、具体实现代码
mysql创建文件表
由于我们限制上传文件大小为500mb,换算为字节B不超过10的9次方,所以file_size选择decimal长度为9,0
上传文件的代码
public void uploadFile(MultipartFile multipartFile, Integer userId) { //设定的总容量,单位为GB
BigDecimal Capacity = new BigDecimal(30);
//校验当前用户总size+当前文件size是否超出容量,若超出则抛出异常
//添加分布式锁
String lockKey = "LOCK_TOTAL_FILE_SIZE" + userId;
RLock lock = redissonClient.getLock(lockKey);
try {
boolean res = lock.tryLock(30, TimeUnit.SECONDS);
if (res) {
BigDecimal userTotalSize;//当前用户文件总size,单位为字节 B
BigDecimal fileSize = new BigDecimal(multipartFile.getSize());//当前上传文件size
BigDecimal mod = new BigDecimal(1024);//GB转B的单位
//从redis中获取当前用户文件总size
String Key = "TOTAL_FILE_SIZE" + userId;
Object redisUserTotalSize = redisTemplate.opsForValue().get(Key);
if (redisUserTotalSize != null) {
userTotalSize = new BigDecimal(redisUserTotalSize.toString());
} else {//redis中不存在,则根据数据库查询计算
userTotalSize = FileDao.sumFileSizeByUserId(userId);
redisTemplate.opsForValue().set(Key, userTotalSize.longValue());
}
//-1:总size<容量;0:总size=容量;1:总size>容量;
int re = (userTotalSize.add(fileSize)).compareTo(Capacity.multiply(mod).multiply(mod).multiply(mod));
if (re == 1) {
throw Error("当前空间容量不足");
}
//未超出容量,进行上传文件插入记录等相关操作(通常都是上传云端然后记录url)
......
}
}catch (InterruptedException e) {
throw Error("服务器繁忙,请稍后重试");
} finally {
if(lock.isLocked() && lock.isHeldByCurrentThread()){
lock.unlock();
}
}
}
从库中查询当前用户文件总size代码
@Select("SELECT SUM(file_size) FROM file where user_id = #{userId}")BigDecimal sumFileSizeByUserId(Integer userId);
删除文件的代码
public void deleteFile(Integer id,Integer userId,Long filesize) { //添加分布式锁,更新redis中空间文件总size
String lockKey = "LOCK_TOTAL_FILE_SIZE"+userId;
RLock lock = redissonClient.getLock(lockKey);
try {
boolean res = lock.tryLock(30, TimeUnit.SECONDS);
if(res) {
//删除云端文件
......
//若redis中有空间文件总size就更新,没有的话不用管
String Key = "TOTAL_FILE_SIZE"+userId;
if(redisTemplate.hasKey(Key)){
redisTemplate.opsForValue().decrement(Key,filesize);
}
//删除文件记录表中对应数据
FileDao.deleteById(id);
} else {
throw Error("服务器繁忙,请稍后重试");
}
} catch (InterruptedException e) {
throw Error("服务器繁忙,请稍后重试");
} finally {
if(lock.isLocked() && lock.isHeldByCurrentThread()){
lock.unlock();
}
}
}