目录

一、总体思路

二、获取文件大小的方式

三、BigDecimal

四、具体实现代码

mysql创建文件表

上传文件的代码

从库中查询当前用户文件总size代码

 删除文件的代码


一、总体思路

首先有一张文件记录表,记录每个上传到云端的文件对应的用户id、文件大小(单位为B)、文件url等信息 ;

文件的总size根据表内该用户的所有文件记录的file_size字段累加得到,将这个值存入redis;

每次上传文件的时候,先获取本次文件的size,再用redisson进行加锁,去redis中取到该用户所有文件总size,再设定的值进行比较,如果小于设定值则正常插入数据,大于则抛出异常

二、获取文件大小的方式

一般和前后端进行文件交互,都是通过MultipartFile实现的,它有一个方法:getSize()

无需参数,返回的是该文件Long类型的字节。

我们可以通过它获取到上传的文件的原字节

redis used_memory 限制 redis容量限制_文件大小

我本地有一个文件,可以看到它的文件大小,一会我就用它进行测试:

redis used_memory 限制 redis容量限制_java_02

 

然和测试接口,就用这个文件:

redis used_memory 限制 redis容量限制_文件大小_03

redis used_memory 限制 redis容量限制_文件大小_04

可以发现,获取到的文件大小和原字节是一模一样的

redis used_memory 限制 redis容量限制_java_05

虽然可以获取到文件的原字节大小,但是有的需求是需要我们把这个字节进行单位转换的。

这时候需要我们一个方法,把获取到的文件字节传进去,然后返回的就是字节转换后的文件大小

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;
     }


 

redis used_memory 限制 redis容量限制_当前用户_06

三、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 

redis used_memory 限制 redis容量限制_当前用户_07

上传文件的代码

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();
        }
    }
}