当前位置: 首页 > news >正文

【网盘系统】递归删除批量文件、从回收站恢复文件、彻底删除文件

为何需要用到递归?

在网盘系统中,文件的类型分为文件和文件夹两种类型。当我们想要批量删除文件时,不乏其中会包含文件夹,而想要删除这个文件夹,自然其中所包含的文件都要删除,而其中所包含的文件也有可能是文件夹,所以需要用到递归。

将文件移进回收站的代码实现(Service层)

    /*** 将文件移到回收站* @param fileIds* @param userId*/@Overridepublic void removeFileToRecycleBin(String fileIds, String userId) {String[] fileIdArray = fileIds.split(",");LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(FileInfo::getUserId, userId).in(FileInfo::getFileId, Arrays.asList(fileIdArray)).eq(FileInfo::getDelFlag, FileDelFlagEnums.USING.getFlag());List<FileInfo> fileInfoList = this.baseMapper.selectList(queryWrapper);if(fileInfoList.isEmpty()){return;}// 待删除的所有目录IdList<String> allSubFolderList = new ArrayList<>();for (FileInfo fileInfo : fileInfoList) {if(fileInfo.getFolderType().equals(FileFolderTypeEnums.FOLDER.getType())){findAllSubFolderList(allSubFolderList, userId, fileInfo.getFileId(), FileDelFlagEnums.USING.getFlag());}}if(!allSubFolderList.isEmpty()){// 将这些目录下的所有文件标记为“已删除”LambdaUpdateWrapper<FileInfo> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(FileInfo::getUserId, userId).in(FileInfo::getFilePid, allSubFolderList).eq(FileInfo::getDelFlag, FileDelFlagEnums.USING.getFlag());FileInfo updateFileInfo = new FileInfo();updateFileInfo.setDelFlag(FileDelFlagEnums.DEL.getFlag());this.baseMapper.update(updateFileInfo, updateWrapper);}// 将选中的文件标记为“回收站”List<String> delFileIdList = Arrays.asList(fileIdArray);LambdaUpdateWrapper<FileInfo> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(FileInfo::getUserId, userId).in(FileInfo::getFileId, delFileIdList);FileInfo updateFileInfo = new FileInfo();updateFileInfo.setDelFlag(FileDelFlagEnums.RECYCLE.getFlag());updateFileInfo.setRecoveryTime(new Date());this.baseMapper.update(updateFileInfo, updateWrapper);}

文件的DelFlag类型有三种,使用中、回收站、删除。只有选中的文件会被标记为“回收站”,而选中的文件夹内包含的文件则是标记为“已删除”。

为什么要这么设计呢?

可以参考我们windows的回收站,当我们把一个文件夹扔进回收站后,会发现在回收站中是不能把这个文件夹打开,自然也就无法看到文件夹中的文件了,所以文件夹中的文件就不需要标记为"回收站",直接标记为“已删除”。当我们需要去恢复时,只需要将那些父id为这个文件夹的文件重新标记为"使用中"即可。

上述代码中用到的"findAllSubFolderList"就是递归查找目标目录下的所有子目录的方法,这个方法不仅在此处用到,后续恢复文件和彻底删除文件也会用到,让我们看看实现的代码:

    /*** 递归查找目标目录下的所有目录* @param fileIdList* @param userId* @param fileId* @param delFlag*/private void findAllSubFolderList(List<String> fileIdList, String userId, String fileId, Integer delFlag){fileIdList.add(fileId);LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(FileInfo::getUserId, userId).eq(FileInfo::getFilePid, fileId).eq(FileInfo::getDelFlag, delFlag).eq(FileInfo::getFolderType, FileFolderTypeEnums.FOLDER.getType());//  找到这个目录的所有子目录List<FileInfo> fileInfoList = this.baseMapper.selectList(queryWrapper);// 递归查找所有子目录下的所有文件for (FileInfo fileInfo : fileInfoList) {findAllSubFolderList(fileIdList, userId, fileInfo.getFileId(), delFlag);}}

从回收站恢复文件的代码实现(Service层)

    /*** 从回收站恢复文件* @param fileIds* @param userId*/@Override@Transactional(rollbackFor = Exception.class)public void recoverFileBatch(String fileIds, String userId) {String[] fileIdArray = fileIds.split(",");LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(FileInfo::getUserId, userId).eq(FileInfo::getDelFlag, FileDelFlagEnums.RECYCLE.getFlag()).in(FileInfo::getFileId, Arrays.asList(fileIdArray));List<FileInfo> fileInfoList = this.baseMapper.selectList(queryWrapper);if(fileInfoList.isEmpty()){return;}// 待恢复的所有目录IdList<String> allSubFolderList = new ArrayList<>();for (FileInfo fileInfo : fileInfoList) {if(fileInfo.getFolderType().equals(FileFolderTypeEnums.FOLDER.getType())){findAllSubFolderList(allSubFolderList, userId, fileInfo.getFileId(), FileDelFlagEnums.DEL.getFlag());}}// 查询所有根目录文件queryWrapper.clear();queryWrapper.eq(FileInfo::getUserId, userId).eq(FileInfo::getDelFlag, FileDelFlagEnums.USING.getFlag()).eq(FileInfo::getFilePid, Constants.ZERO_STR);List<FileInfo> rootFileList = this.baseMapper.selectList(queryWrapper);Map<String, FileInfo> rootFileNameMap = rootFileList.stream().collect(Collectors.toMap(FileInfo::getFileName, Function.identity(), (k1, k2)->k2));// 查询目录下所有被删除的文件,将目录下的所有被删除的文件更新为“使用中”if(!allSubFolderList.isEmpty()){FileInfo updateFileInfo = new FileInfo();updateFileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());LambdaUpdateWrapper<FileInfo> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(FileInfo::getUserId, userId).eq(FileInfo::getDelFlag, FileDelFlagEnums.DEL.getFlag()).in(FileInfo::getFilePid, allSubFolderList);this.baseMapper.update(updateFileInfo, updateWrapper);}// 将选中的文件恢复到“使用中”,且父级目录设置为根目录List<String> delFileIdList = Arrays.asList(fileIdArray);FileInfo updateFileInfo = new FileInfo();updateFileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());updateFileInfo.setFilePid(Constants.ZERO_STR);updateFileInfo.setLastUpdateTime(new Date());LambdaUpdateWrapper<FileInfo> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(FileInfo::getUserId, userId).eq(FileInfo::getDelFlag, FileDelFlagEnums.RECYCLE.getFlag()).in(FileInfo::getFileId, delFileIdList);this.baseMapper.update(updateFileInfo, updateWrapper);// 将所选文件重命名for (FileInfo fileInfo : fileInfoList) {FileInfo rootFileInfo = rootFileNameMap.get(fileInfo.getFileName());// 根目录下已经存在同名文件,需要进行重命名if(rootFileInfo != null){String newFileName = StringTools.rename(fileInfo.getFileName());this.baseMapper.updateFileNameByFileIdAndUserId(fileInfo.getFileId(), newFileName, userId);}}}

在我的网盘系统中,所有从回收站被恢复的文件都会被恢复到根目录中,不然移进回收站的时候还要记录下原先的文件路径,而原先的文件路径很可能在恢复之前已经被删除了,所以选择默认恢复到根目录中。

当然,在将回收站的文件恢复到根目录之前,还要检查是否需要重命名,因为可能与根目录中已存在的文件发生重名。

彻底删除文件的代码实现(Service层)

    /*** 彻底删除文件* @param fileIds* @param userId* @param adminOp*/@Override@Transactional(rollbackFor = Exception.class)public void delFileBatch(String fileIds, String userId, Boolean adminOp) {String[] fileIdArray = fileIds.split(",");LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(FileInfo::getUserId, userId).eq(FileInfo::getDelFlag, FileDelFlagEnums.RECYCLE.getFlag()).in(FileInfo::getFileId, Arrays.asList(fileIdArray));List<FileInfo> fileInfoList = this.baseMapper.selectList(queryWrapper);if(fileInfoList.isEmpty()){return;}List<String> allSubFolderList = new ArrayList<>();// 找到所选文件子目录文件Idfor (FileInfo fileInfo : fileInfoList) {if(fileInfo.getFolderType().equals(FileFolderTypeEnums.FOLDER.getType())){findAllSubFolderList(allSubFolderList, userId, fileInfo.getFileId(), FileDelFlagEnums.DEL.getFlag());}}// 删除所选文件子目录中的文件if(!allSubFolderList.isEmpty()){LambdaQueryWrapper<FileInfo> delWrapper = new LambdaQueryWrapper<>();delWrapper.eq(FileInfo::getUserId, userId).in(FileInfo::getFilePid, allSubFolderList);if(!adminOp){delWrapper.eq(FileInfo::getDelFlag, FileDelFlagEnums.DEL.getFlag());}this.baseMapper.delete(delWrapper); // 从数据库中彻底删除}// 删除所选文件LambdaQueryWrapper<FileInfo> delWrapper = new LambdaQueryWrapper<>();delWrapper.eq(FileInfo::getUserId, userId).in(FileInfo::getFileId, Arrays.asList(fileIdArray));if(!adminOp){delWrapper.eq(FileInfo::getDelFlag, FileDelFlagEnums.RECYCLE.getFlag());}this.baseMapper.delete(delWrapper);// 更新用户使用空间Long useSpace = this.baseMapper.selectUseSpaceByUserId(userId);UserInfo userInfo = new UserInfo();userInfo.setUseSpace(useSpace);LambdaQueryWrapper<UserInfo> userQueryWrapper = new LambdaQueryWrapper<>();userQueryWrapper.eq(UserInfo::getUserId, userId);userInfoMapper.update(userInfo, userQueryWrapper);// 更新缓存UserSpaceDto userSpaceDto = redisComponent.getUserSpaceUse(userId);userSpaceDto.setUseSpace(useSpace);redisComponent.saveUserSpaceUse(userId, userSpaceDto);}

彻底删除的方法有两种人会调用,一种是用户彻底删除自己在回收站中的文件,一种是管理用彻底删除某个文件。区别在于如果是用户,那么只能彻底删除在回收站中的文件,所以需要传入一个adminOp的参数,如果adminOp为false,也就是用户,那么必须校验文件的DelFlag是否为RECYCLE(文件处于回收站)。

在彻底删除文件之后应当更新用户的使用空间(归还用户使用空间),同时要把Redis中的缓存(用户使用空间)进行更新。


http://www.mrgr.cn/news/79356.html

相关文章:

  • AI智算-k8s部署大语言模型管理工具Ollama
  • 【Linux】vi/vim 使用技巧
  • 《HTML 的变革之路:从过去到未来》
  • 前端缓存页面处理方法
  • React和Vue.js的相似性和差异性是什么?
  • kubeadm安装K8s集群之高可用组件keepalived+nginx
  • PS核心知识点
  • 【组件封装】uniapp vue3 封装一个完整的Tabs(标签页)组件教程,功能由简到杂实现讲解。
  • Python实现BBS论坛自动签到【steamtools论坛】
  • 2023 年“泰迪杯”数据分析技能赛B 题企业财务数据分析与造假识别
  • Java后端面试模板(技术面)
  • [软件工程]十.可靠性工程(reliable engineering)
  • android studio 读写文件操作(应用场景二)
  • 代码随想录-算法训练营day41(动态规划04:01背包,01背包滚动数组,分割等和子集)
  • 信奥常考点:二叉树的构建(已知中序和 前序或后序 的情况下)
  • 网络原理之 TCP 协议
  • pubmed关键词搜索技能1:待更新
  • No APK Signature Scheme v2 signature in package
  • T C P
  • 删除asmlib磁盘导致磁盘组故障恢复---惜分飞
  • HBU深度学习实验14.5-循环神经网络(1.5)
  • 2024 年(第 12 届)“泰迪杯”数据挖掘挑战赛—— C 题:竞赛论文的辅助自动评阅
  • 基于RISC-V的HSM方案
  • ruoyi-nbcio为安全起见actuator为仅暴露health端点
  • Spark实训
  • C++中参数传递方式介绍