【EasyPan】saveShare代码分析
【EasyPan】项目常见问题解答(自用&持续更新中…)汇总版
保存分享文件到个人网盘代码分析
一、代码结构概览
该代码实现了一个将他人分享的文件保存到自己网盘的功能,主要分为三个部分:
- 控制器层(Controller):处理HTTP请求和响应
- 服务层(Service):实现核心业务逻辑
- 递归方法:处理目录结构的深度复制
二、控制器层分析
saveShare
方法
@RequestMapping("/saveShare")
@GlobalInterceptor(checkParams = true, checkLogin = false)
public ResponseVO saveShare(HttpSession session,@VerifyParam(required = true) String shareId,@VerifyParam(required = true) String shareFileIds,@VerifyParam(required = true) String myFolderId) {// 验证分享有效性SessionShareDto shareSessionDto = checkShare(session, shareId);// 获取当前用户信息SessionWebUserDto webUserDto = getUserInfoFromSession(session);// 防止自分享操作if (shareSessionDto.getShareUserId().equals(webUserDto.getUserId())) {throw new BusinessException("自己分享的文件无法保存到自己的网盘");}// 调用服务层保存逻辑fileInfoService.saveShare(shareSessionDto.getFileId(), shareFileIds, myFolderId, shareSessionDto.getShareUserId(), webUserDto.getUserId());return getSuccessResponseVO(null);
}
三、服务层分析
saveShare
方法
@Override
public void saveShare(String shareRootFilePid, String shareFileIds, String myFolderId, String shareUserId, String currentUserId) {// 分割文件ID列表String[] shareFileIdArray = shareFileIds.split(",");// 查询目标文件夹现有文件(用于重名检查)FileInfoQuery fileInfoQuery = new FileInfoQuery();fileInfoQuery.setUserId(currentUserId);fileInfoQuery.setFilePid(myFolderId);List<FileInfo> currentFileList = this.fileInfoMapper.selectList(fileInfoQuery);// 构建文件名映射表Map<String, FileInfo> currentFileMap = currentFileList.stream().collect(Collectors.toMap(FileInfo::getFileName, Function.identity(), (data1,data2)->data2));// 查询待保存的分享文件列表fileInfoQuery = new FileInfoQuery();fileInfoQuery.setUserId(shareUserId);fileInfoQuery.setFileIdArray(shareFileIdArray);List<FileInfo> shareFileList = this.fileInfoMapper.selectList(fileInfoQuery);// 准备拷贝文件列表List<FileInfo> copyFileList = new ArrayList<>();Date curDate = new Date();// 处理每个待保存文件for (FileInfo item : shareFileList) {// 处理重名文件FileInfo haveFile = currentFileMap.get(item.getFileName());if (haveFile != null) {item.setFileName(StringTools.rename(item.getFileName()));}// 递归处理文件/目录findAllSubFile(copyFileList, item, shareUserId, currentUserId, curDate, myFolderId);}// 批量插入数据库this.fileInfoMapper.insertBatch(copyFileList);
}
四、递归方法分析
findAllSubFile
方法
private void findAllSubFile(List<FileInfo> copyFileList, FileInfo fileInfo, String sourceUserId, String currentUserId, Date curDate, String newFilePid) {// 保存原始文件ID(用于目录查询)String sourceFileId = fileInfo.getFileId();// 更新文件元信息fileInfo.setCreateTime(curDate);fileInfo.setLastUpdateTime(curDate);fileInfo.setFilePid(newFilePid);fileInfo.setUserId(currentUserId);// 生成新文件IDString newFileId = StringTools.getRandomString(Constants.LENGTH_10);fileInfo.setFileId(newFileId);// 添加到拷贝列表copyFileList.add(fileInfo);// 如果是目录则递归处理子文件if (FileFolderTypeEnums.FOLDER.getType().equals(fileInfo.getFolderType())) {FileInfoQuery query = new FileInfoQuery();query.setFilePid(sourceFileId);query.setUserId(sourceUserId);List<FileInfo> sourceFileList = this.fileInfoMapper.selectList(query);for (FileInfo item : sourceFileList) {findAllSubFile(copyFileList, item, sourceUserId, currentUserId, curDate, newFileId);}}
}
五、安全性和可靠性设计
-
参数校验:
- 使用
@VerifyParam
确保必要参数非空 - 通过
checkShare
验证分享有效性
- 使用
-
数据隔离:
- 严格区分
shareUserId
和currentUserId
- 所有查询都带有用户ID条件
- 严格区分
-
异常处理:
- 自分享操作抛出明确业务异常
- 潜在的数据库异常由全局异常处理器捕获
-
幂等性设计:
- 重名文件自动重命名
- 使用新ID避免冲突