Redisson+Redis+Mysql实现对用户上传文件总容量限制(Java)

目录

一、总体思路

二、获取文件大小的方式

三、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();
        }
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值