文件暂存系统

疫情被封,闲来无事,将之前的一个想法,开发了出来。虽然比较鸡肋。但也记录一下。
真不知道开发文档怎么写,就将自己从思考到上线的一些东西简单记录一下。

一、项目概述

1、项目背景

       因为鄙人上课不喜欢带手机,有时候又忘带优盘。在一些上机课的时候会产生一些资料。我需要一个简单、便捷的方式将该资料暂时存储起来。便于课后整理。于是我脑袋里就冒出了“文件暂存系统”这个玩意。

2、功能概述

       (1)用户通过文件暂存系统上传文件,上传成功后文件暂存系统将返回一段8位提取码,用户可在24小时内通过提取码对该文件进行提取。
       (2)如果用户为了安全起见可以在上传的时候输入密钥对文件进行加密存储。
       (3)系统会在24小时后删除文件。
演示截图
上传页面
在这里插入图片描述
提取页面
在这里插入图片描述

二、概要设计

1、系统架构设计

(1)系统采用前后端分离(纯粹是为了练手)。
(2)文件的存储是直接存储到服务器上的(文件会经过处理,防止上传木马等)。
(3)因为不涉及登录注册,只有提取码和密钥,所以只采用非关系型数据库redis进行提取码和密钥的存储。
(4)为了方便,我的redis、后端项目、前端项目都是部署到一台服务器的,redis和nginx是通过docker进行部署的。
在这里插入图片描述

2、技术选型

后端:
(1)SpringBoot搭建后台整体架构。
(2)redis进行提取码和密钥的存储。
前端:
(1)采用vue、element-ui进行界面的搭建。
(2)采用axios进行请求与响应。

3、前端概要设计

前端主要分为两大模块。

文件上传模块
       用户进行文件上传,选择是否加密,上传成功后返回提取码。
在这里插入图片描述
在这里插入图片描述

文件下载模块
       用户输入提取码,如果文件在上传时加了密,则输入自定义密钥。输入正确后点击提取,下载文件。
在这里插入图片描述

4、后端概要设计

后端主要分为三大模块。

redis数据存储结构
一个文件对应两个键值对
提取码+fileName:文件名
提取码+filePwd:密钥(有默认加密密钥)

文件上传模块
       接收上传到的文件、密钥。对文件进行加密存储,并返回新文件名。生成提取码,存储“提取码+fileName:文件名”、“提取码+filePwd:密钥”。如果存储成功则返回成功提示,否则返回失败提示。

文件下载模块
       接收到上传的提取码、密钥,从redis中进行比对,比对正确后下载文件。

文件定时删除模块
       每2分钟从redis中进行一次检查,从存储文件的目录删除redis中不存在的文件名。

详细设计

前端

1、上传

在这里插入图片描述
(1)数据定义。
在这里插入图片描述

(2)通过el-upload组件进行文件的上传。
在这里插入图片描述
(3)给on-change属性绑定函数changeFile,获取上传的文件。
在这里插入图片描述
在这里插入图片描述
(4)将上传的文件对象,赋给自定义的数据file。
在这里插入图片描述

在这里插入图片描述
(5)当点击提交按钮时,将数据添加到formData中,然后使用axios发送post请求。

数据封装
在这里插入图片描述
发送请求
在这里插入图片描述
在这里插入图片描述

2、下载

在这里插入图片描述

(1)数据定义。
在这里插入图片描述
(2)点击提取按钮时,校验提取码和密钥然后发请求去后端校验。
在这里插入图片描述
在这里插入图片描述
(3)校验合格后则下载文件。
在这里插入图片描述

3、其它功能

如输入框得到焦点提示、上传成功提示、正则表达式校验密钥等就不赘述了。

后端

1、文件上传

(1)通过FileController的upload方法接收文件和密钥。
在这里插入图片描述
(2)UploadService的upload方法进行文件的存储。
在这里插入图片描述

@Override
    public String upload(MultipartFile file, String filePwd) throws IOException {

        //1、如果没有携带密钥就使用默认密钥(给所有文件都加密,防止病毒文件)
        if (filePwd.length()==0){
            filePwd=defPwd;
        }

        //2、防止basePath文件夹不存在(防止存储文件的文件夹不存在)
        File dir = new File(basePath);
        if(!dir.exists()){
            //目录不存在,需要创建
            dir.mkdirs();
        }

        //3、获取新文件名(传入MultipartFile,通过uuid生成新的文件名称,拼接上之前的后缀返回)
        String newFileName = FileUtils.newFileName(file);//传入文件

        //4、加密转存到目的文件夹(参数1,参数2,参数3)=》(输入流,存储的位置,将4位串变成密钥)
        FileUtils.jia(file.getInputStream(),basePath+newFileName,FileUtils.filepwd(filePwd));

        //5、生成提取码,并返回
        //截取新名称的前八位提取码
        String extKey = newFileName.substring(0, 8);

        //4、存储到redis
        //存储文件名   键=提取码:fileName 值=新文件名
        redisTemplate.opsForValue().set(extKey+":fileName",newFileName,24, TimeUnit.HOURS);
        //存储加密文件的密钥   键=提取码:filePwd 值=加密密钥
        redisTemplate.opsForValue().set(extKey+":filePwd",filePwd,24, TimeUnit.HOURS);

        return extKey;
    }

2、文件下载

(1)通过FileController的downloadVerify方法接收提取码和密钥。
在这里插入图片描述
(2)通过DownloadService的downloadVerify进行文件提取码和密钥的校验。如果合法则返回提取码和密钥。

public R<Object> downloadVerify(String extKey, String filePwd_) {

        Object fileName = redisTemplate.opsForValue().get(extKey + ":fileName");
        Object filePwd = redisTemplate.opsForValue().get(extKey + ":filePwd");


        //1、filePwd.length==0
        //(1)有键extKey
        //   下载文件并饭返回
        //(2)没有键extKey
        //   返回“提取失败”

        //2、filePwd.length!=0
        //(1)有键extKey和filePwd且输入的和存储的相等
        //   下载文件并饭返回
        //(2)没有键extKey||filePwd
        //   返回“提取失败”

        //存储返回结果
        R<Object> resultR = new R<>();

        if (filePwd_.length()==0){//密钥是系统给的

            if (fileName!=null&&defPwd.equals(filePwd)){

                //提取
//                System.out.println("无密钥且合法");
                resultR.setData(1);//data=1,无密钥且合法
                resultR.add("extKey",extKey);

//                return resultR;
            }else{
                if (defPwd.equals(filePwd)){
//                    System.out.println("提取码错误");

                    resultR.setData(0);//错误
                    resultR.setMsg("提取码错误");
                }else{
                    if (fileName==null){
//                        System.out.println("提取码、密钥错误");

                        resultR.setData(0);//错误
                        resultR.setMsg("提取码、密钥错误");
                    }else{
//                        System.out.println("密钥错误");

                        resultR.setData(0);//错误
                        resultR.setMsg("密钥错误");

                    }
                }

            }

        }else{
            //fileName==null:提取码错误
            //!filePwd.toString().equals(filePwd_):密钥错误
            if (fileName!=null&&filePwd_.equals(filePwd)){
                //合法
                System.out.println("有密钥且合法");

//                FileUtils.jie(basePath+fileName,filePwd.toString(),response);

                //提取
                System.out.println("有密钥且合法");
                resultR.setData(2);//data=2,无密钥且合法
                resultR.add("extKey",extKey);
                resultR.add("filePwd",filePwd);

            }else{
                if (filePwd_.equals(filePwd)){
                    System.out.println("提取码错误");

                    resultR.setData(0);//错误
                    resultR.setMsg("提取码错误");


                }else{
                    if (fileName==null){
                        System.out.println("提取码、密钥错误");

                        resultR.setData(0);//错误
                        resultR.setMsg("提取码、密钥错误");
                    }else{
                        System.out.println("密钥错误");

                        resultR.setData(0);//错误
                        resultR.setMsg("密钥错误");
                    }
                }
            }


        }

        return resultR;

    }

(3)通过FileController的download方法接收下载请求。
在这里插入图片描述
(4)通过DownloadService的download方法进行下载。

@Override
    public void download(String extKey, String filePwd_) {

        Object fileName = redisTemplate.opsForValue().get(extKey + ":fileName");
        Object filePwd = redisTemplate.opsForValue().get(extKey + ":filePwd");
        //解密并下载
        FileUtils.jie(basePath+fileName,filePwd.toString(),response);

    }

3、定时删除

采用SpringBoot提供的@Scheduled进行定时执行,基本逻辑为,每2分钟获取redis中的文件名的集合A和文件夹中的文件名B,保留文件夹中的A∩B。其余删除。

@Service//定时删除服务
public class TimingDeleteImpl implements TimingDelete {

    @Autowired
    RedisTemplate redisTemplate;

    @Value("${file.basePath}")
    private String basePath;

    @Override
    @Scheduled(cron = "0 0/2 * * * ?")//每两分钟检查一次
    public void timingDelete() {
        //1、查询出redis中所有的文件名A(集合)
        Set keys = redisTemplate.keys("*:fileName");
        List values = redisTemplate.opsForValue().multiGet(keys);

        //2、查询出目录下的所有的文件名B(集合)
        File file = new File(basePath);
        File[] files = file.listFiles();
        List<String> fileName = new ArrayList<String>();
        for (File f : files) {
            fileName.add(f.getName());
        }

        //3、B-A
        fileName.removeAll(values);

        //4、删除B-A
        for (String sql01 : fileName) {
            File file1 = new File(basePath + sql01);
            file1.delete();
        }

    }

项目不足

1、无法抵挡暴力破解,无安全策略。
2、文件加密方法过于简单。
3、无法抵挡恶意上传。
4、axios的异步响应存在bug。
5、单文件上传。
6、不可大文件上传(会响应超时)。
还有很多暂时想不起。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

齊 天 大 聖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值