基于Springboot + vue的云盘系统

news2024/11/30 10:47:02

目录

  • 一. 🦁 前言
  • 二. 🦁 主要技术栈
  • 三. 🦁 架构搭建
    • 1. 项目搭建效果
    • 2. 各部分作用
  • 四. 🦁 主要功能
    • 1.功能图
    • 2. 主要功能
      • 2.1 分片上传文件
      • 2.2 存储分享记录
  • 五. 🦁 效果显示

一. 🦁 前言

本项目是在B站学习的项目改造而来,主要是一个模仿百度网盘的上传文件、下载文件功能以及网盘分享文件功能,希望你喜欢。

本系统是一个文件存储和共享平台,提供了强大的功能和可靠的数据保护,方便用户随时随地进行文件的管理和分享。本系统基于Springboot+Vue实现,操作简便,界面美观,拥有良好的用户体验和稳定的性能。无论是个人用户还是团队合作,都能够在此享受到高效和便捷的文件存储和共享服务。

二. 🦁 主要技术栈

  1. 后端
    SpringBootMySQLMybatisSpringMVCRedisffmpeg
  2. 前端
    Vue3Element-Plus

三. 🦁 架构搭建

tips:
该项目的项目结构改编参考了天罡大佬的《Maven 三层项目结构搭建》一文(天罡大佬人很好,很热心解答狮子疑惑的问题,推荐关注哦!!!),但是又有写不同的地方,狮子根据自己的理解整理成如下结构:

image-20230428112738573

1. 项目搭建效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 各部分作用

  • 表现层: web层,提供controller,处理http请求,提供给前端的API;
  • 业务逻辑层: service层,和业务相关,主要处理业务逻辑;
  • 数据访问层: dal层,做数据访问,主要使用Mybatis与MySQL交互数据;
  • 通用层:common层,主要存放实体类,常量,工具,异常类等。

四. 🦁 主要功能

1.功能图

云盘系统

2. 主要功能

2.1 分片上传文件

这个方法是本系统的核心功能,前端将文件分片后将参数传回,后台接收上传文件的参数,将文件暂存在临时目录中。如果是第一个分片,则判断是否存在相同的文件(根据文件的 MD5 值判断),如果存在相同的文件,则将其直接作为秒传处理,否则继续将文件保存在临时目录中。如果是最后一个分片,则将其记录在数据库中,并异步调用文件合并的方法。在整个上传过程中,还需要进行磁盘空间和用户可用空间的判断。最后返回上传结果。

    @Transactional(rollbackFor = Exception.class)
    public UploadResultDto uploadFile(SessionWebUserDto webUserDto, String fileId, MultipartFile file, String fileName, String filePid, String fileMd5,
                                      Integer chunkIndex,
                                      Integer chunks) {
        File tempFileFolder = null;
        Boolean uploadSuccess = true;
        try {
            UploadResultDto resultDto = new UploadResultDto();
            if (StringTools.isEmpty(fileId)) {
                fileId = StringTools.getRandomString(Constants.LENGTH_10);
            }
            resultDto.setFileId(fileId);
            Date curDate = new Date();
            UserSpaceDto spaceDto = redisComponent.getUserSpaceUse(webUserDto.getUserId());
            if (chunkIndex == 0) {
                FileInfoQuery infoQuery = new FileInfoQuery();
                infoQuery.setFileMd5(fileMd5);
                infoQuery.setSimplePage(new SimplePage(0, 1));
                infoQuery.setStatus(FileStatusEnums.USING.getStatus());
                List<FileInfo> dbFileList = this.fileInfoMapper.selectList(infoQuery);
                //秒传
                if (!dbFileList.isEmpty()) {
                    FileInfo dbFile = dbFileList.get(0);
                    //判断文件状态
                    if (dbFile.getFileSize() + spaceDto.getUseSpace() > spaceDto.getTotalSpace()) {
                        throw new BusinessException(ResponseCodeEnum.CODE_904);
                    }
                    dbFile.setFileId(fileId);
                    dbFile.setFilePid(filePid);
                    dbFile.setUserId(webUserDto.getUserId());
                    dbFile.setFileMd5(null);
                    dbFile.setCreateTime(curDate);
                    dbFile.setLastUpdateTime(curDate);
                    dbFile.setStatus(FileStatusEnums.USING.getStatus());
                    dbFile.setDelFlag(FileDelFlagEnums.USING.getFlag());
                    dbFile.setFileMd5(fileMd5);
                    fileName = autoRename(filePid, webUserDto.getUserId(), fileName);
                    dbFile.setFileName(fileName);
                    this.fileInfoMapper.insert(dbFile);
                    resultDto.setStatus(UploadStatusEnums.UPLOAD_SECONDS.getCode());
                    //更新用户空间使用
                    updateUserSpace(webUserDto, dbFile.getFileSize());

                    return resultDto;
                }
            }
            //暂存在临时目录
            String tempFolderName = appConfig.getProjectFolder() + Constants.FILE_FOLDER_TEMP;
            String currentUserFolderName = webUserDto.getUserId() + fileId;
            //创建临时目录
            tempFileFolder = new File(tempFolderName + currentUserFolderName);
            if (!tempFileFolder.exists()) {
                tempFileFolder.mkdirs();
            }

            //判断磁盘空间
            Long currentTempSize = redisComponent.getFileTempSize(webUserDto.getUserId(), fileId);
            if (file.getSize() + currentTempSize + spaceDto.getUseSpace() > spaceDto.getTotalSpace()) {
                throw new BusinessException(ResponseCodeEnum.CODE_904);
            }

            File newFile = new File(tempFileFolder.getPath() + "/" + chunkIndex);
            file.transferTo(newFile);

            //保存临时大小
            redisComponent.saveFileTempSize(webUserDto.getUserId(), fileId, file.getSize());
            //不是最后一个分片,直接返回
            if (chunkIndex < chunks - 1) {
                resultDto.setStatus(UploadStatusEnums.UPLOADING.getCode());
                return resultDto;
            }
            redisComponent.saveFileTempSize(webUserDto.getUserId(), fileId, file.getSize());
            //最后一个分片上传完成,记录数据库,异步合并分片
            String month = DateUtil.format(curDate, DateTimePatternEnum.YYYYMM.getPattern());
            String fileSuffix = StringTools.getFileSuffix(fileName);
            //真实文件名
            String realFileName = currentUserFolderName + fileSuffix;
            FileTypeEnums fileTypeEnum = FileTypeEnums.getFileTypeBySuffix(fileSuffix);
            //自动重命名
            fileName = autoRename(filePid, webUserDto.getUserId(), fileName);
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileId(fileId);
            fileInfo.setUserId(webUserDto.getUserId());
            fileInfo.setFileMd5(fileMd5);
            fileInfo.setFileName(fileName);
            fileInfo.setFilePath(month + "/" + realFileName);
            fileInfo.setFilePid(filePid);
            fileInfo.setCreateTime(curDate);
            fileInfo.setLastUpdateTime(curDate);
            fileInfo.setFileCategory(fileTypeEnum.getCategory().getCategory());
            fileInfo.setFileType(fileTypeEnum.getType());
            fileInfo.setStatus(FileStatusEnums.TRANSFER.getStatus());
            fileInfo.setFolderType(FileFolderTypeEnums.FILE.getType());
            fileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());
            this.fileInfoMapper.insert(fileInfo);

            Long totalSize = redisComponent.getFileTempSize(webUserDto.getUserId(), fileId);
            updateUserSpace(webUserDto, totalSize);

            resultDto.setStatus(UploadStatusEnums.UPLOAD_FINISH.getCode());
            //事务提交后调用异步方法
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    fileInfoService.transferFile(fileInfo.getFileId(), webUserDto);
                }
            });
            return resultDto;
        } catch (BusinessException e) {
            uploadSuccess = false;
            logger.error("文件上传失败", e);
            throw e;
        } catch (Exception e) {
            uploadSuccess = false;
            logger.error("文件上传失败", e);
            throw new BusinessException("文件上传失败");
        } finally {
            //如果上传失败,清除临时目录
            if (tempFileFolder != null && !uploadSuccess) {
                try {
                    FileUtils.deleteDirectory(tempFileFolder);
                } catch (IOException e) {
                    logger.error("删除临时目录失败");
                }
            }
        }
    }

2.2 存储分享记录

这个方法用于存储分享记录,方法签名为public void saveShare(FileShare share)。方法的输入参数为一个FileShare对象。

在方法内部,首先根据share对象中的validType属性获取对应的ShareValidTypeEnums枚举值,如果无法获取该枚举值,则抛出BusinessException异常并返回错误码ResponseCodeEnum.CODE_600。

接着,根据validType属性判断分享是否永久有效,如果不是,则设置该分享的过期时间为当前时间加上对应的天数。

然后,获取当前时间作为分享时间,并为分享记录设置随机的分享ID和提取码(如果提取码为空)。

最后,将分享记录保存至数据库中。

    @Override
    public void saveShare(FileShare share) {
        ShareValidTypeEnums typeEnum = ShareValidTypeEnums.getByType(share.getValidType());
        if (null == typeEnum) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        if (typeEnum != ShareValidTypeEnums.FOREVER) {
            share.setExpireTime(DateUtil.getAfterDate(typeEnum.getDays()));
        }
        Date curDate = new Date();
        share.setShareTime(curDate);
        if (StringTools.isEmpty(share.getCode())) {
            share.setCode(StringTools.getRandomString(Constants.LENGTH_5));
        }
        share.setShareId(StringTools.getRandomString(Constants.LENGTH_20));
        this.fileShareMapper.insert(share);
    }

五. 🦁 效果显示

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/572065.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SSM 如何使用 RabbitMQ 实现消息队列

SSM 如何使用 RabbitMQ 实现消息队列 简介 在分布式系统中&#xff0c;消息队列是一种常见的通信方式&#xff0c;可以实现不同服务之间的异步通信和解耦。RabbitMQ 是一个开源的消息队列软件&#xff0c;本文将介绍如何在 SSM 框架中使用 RabbitMQ 实现消息队列。 本文将使…

python 自动化学习(三) 句柄获取、模拟按键、opencv安装

一、什么是句柄 句柄是在操作系统中的一种标识符&#xff0c;相当于我们每个人的身份证一样&#xff0c;句柄在电脑中也是有唯一性的&#xff0c;我们启动的每一个程序都有自己的句柄号&#xff0c;表示自己的身份 为什么要说句柄&#xff0c;我们如果想做自动化操作时&#xf…

【数据库复习】第六章 关系数据理论 2

若R∈BCNF 所有非主属性对每一个码都是完全函数依赖 所有的主属性对每一个不包含它的码&#xff0c;也是完全函数依赖 没有任何属性完全函数依赖于非码的任何一组属性 多值依赖 Teaching具有唯一候选码(C&#xff0c;T&#xff0c;B)&#xff0c; 即全码&#xff0c; ∈3NF …

JAVA商城源码-B2B2C商城系统-独立部署,一套源码终身可用

在现在电商迅速占领市场的时代里&#xff0c;选择开发商城系统已经成为了一种趋势&#xff0c;现在开发搭建商城系统有很多编程语言可以选择&#xff0c;目前在电商里市面上受到很多商家企业的喜爱的便是Java商城系统&#xff0c;那为什么要选择Java电商系统呢&#xff1f; 1、…

linuxOPS基础_服务器构成

服务器的重要结构组成 家用电脑组成&#xff1a; CPU、主板、内存条、显卡、硬盘、电源、风扇、网卡、显示器、机箱、键盘鼠标等等。 CPU CPU是电脑的大脑&#xff0c; CPU发展史&#xff1a; 32 位CPU&#xff1a;最大的内存寻址地址2^32&#xff0c;大约4G的大小。 CP…

js 常用函数 push()、pop()、shift()、unshift()、slice()、splice() 等

文章目录 1. join() 函数2.push() 函数3. pop() 函数4.shift() 函数5.unshift() 函数6.sort() 函数7. reverse() 函数8. concat() 函数9.slice() 函数10. splice() 函数11. indexOf() & lastIndexOf() 函数 最近对前端一些函数的用法还不是很熟悉&#xff0c;有一些函数容易…

手持式网络性能测试仪应用于哪些领域及可以完成什么工作?

首先明辰智航国产网络一点通有千兆和万兆以手持式网络性能测试仪&#xff0c;两款仪器可以应用于以下领域&#xff1a; 电信运营商&#xff1a;用于测试网络质量、信号强度、带宽、时延、丢包率等参数&#xff0c;以便优化网络性能和提高用户满意度。 企业网络管理&#xff1a…

【什么是iMessage苹果推?】什么是苹果推信?什么是苹果推?

挑选得当的IM推送平台&#xff1a;选择合用于PC真个IM推送平台 开辟或集成API&#xff1a;依照所选平台的开发文档&#xff0c;利用响应的编程语言&#xff08;如Python、Java等&#xff09;开发或集成API&#xff0c;以便与平台举行交互和节制。API可用于建立、办理和发送消息…

STM32F103 USB实现虚拟串口

STM32F103 USB实现虚拟串口 最近买了一个STM32F103C8T6最小核心板&#xff0c;使用CubeIDE无法识别该芯片&#xff0c;发现该芯片的flash是128Kbytes&#xff0c;ST的标准库是64Kbytes&#xff0c;奇怪啊&#xff01;也许是国产替代的&#xff0c;国产化太先进了&#xff0c;导…

CCIG:智能文档处理「新未来」

文章目录 ⭐️ CCIG大会简介⭐️ 领先世界的智能文档处理技术&#x1f31f; 智能图像处理&#xff1a;为文字识别 "增质提效" 筑基✨ 切边增强 - 提升文档图像质量✨ 弯曲矫正 - 解决图像畸变问题✨ 去摩尔纹 - 保证图像信息完整 &#x1f31f; 图像预处理整体效果展…

汇编基础知识

1.汇编工程流程: 汇编指令--->编译器--->机器码--->计算机 2.汇编语言组成: 1.汇编指令 2.伪指令 3.其他符号 3.存储器: 存放指令与数据的容器,也叫内存. 存储器被划分为多个单元,并且从0开始按钮顺序编号,这些编号视为存储器的存储单元的地址. 4.指令与…

《Cocos Creator游戏实战》老虎机抽奖效果实现思路

在线体验地址 Cocos Creator | SlotMachine Cocos Store 购买地址&#xff08;如果没有显示&#xff0c;那就是还在审核&#xff09;&#xff1a; https://store.cocos.com/app/detail/4958微店购买地址&#xff1a; https://weidian.com/item.html?itemID6338406353运行效果…

平板用什么远程操控电脑

现在的第三方专业远程软件大部分支持跨平台连接&#xff0c;要使用平板电脑远程控制电脑&#xff0c;还是很简单的。一般来说按照以下步骤操作即可。 确保两台设备都连接到互联网 确保您要控制的电脑和平板电脑都通过 Wi-Fi 或移动数据连接到互联网。 安装远程控制应用程序 …

设计事务所项目管理指南

在数字化的浪潮下&#xff0c;各行各业都面临着升级转型的问题。对设计团队而言&#xff0c;传统的管理方式已经无法满足日益前进的团队需求。 设计事务所可能存在的管理问题&#xff1a; 1&#xff0c;项目过程中信息流通慢&#xff0c;成员工作进度无法及时同步&#xff1b; …

结构型设计模式01-装饰模式

✨作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 装饰模式 1、 问题引入 要实现一个简单的个人形象系统&#xff0c;使用控制台输出的形式&#xff0c;简单说明搭配着装 Person pa…

【快应用】响应式布局适配横竖屏或折叠屏

【关键词】 响应式布局、折叠屏、横竖屏 【问题背景】 当前开发者在开发快应用时&#xff0c;往往将designWidth设置为设备屏幕的宽度&#xff0c;这时&#xff0c;应用的内容会随着设备宽度的变大而拉伸显示&#xff0c;导致在大屏、横屏、折叠屏展开时显示效果不好。 在折…

PMP考试应该要如何备考?如何短期通过PMP?

我从新考纲考完下来&#xff0c;3A通过了考试&#xff0c;最开始也被折磨过一段时间&#xff0c;但是后面还是找到了方法&#xff0c;也算有点经验&#xff0c;给大家分享一下吧。 程序猿应该是考PMP里面人最多的&#xff0c;毕竟有一个30大坎&#xff0c;大部分人还是考虑转型…

微信小程序button按钮设置宽度无效

button按钮设置宽度无效 背景&#xff1a; 在开发小程序的过程中&#xff0c;遇到了button按钮设置宽度无效的问题 微信客户端 7.0 开始&#xff0c;UI 界面进行了大改版。小程序也进行了基础组件的样式升级&#xff0c;涉及的组件有 button,icon,radio,checkbox,switch,sli…

手把手教你在昇腾平台上搭建PyTorch训练环境

PyTorch是业界流行的深度学习框架&#xff0c;用于开发深度学习训练脚本&#xff0c;默认运行在CPU/GPU上。在昇腾AI处理器上运行PyTorch业务时&#xff0c;需要搭建异构计算架构CANN&#xff08;Compute Architecture for Neural Networks&#xff09;软件开发环境&#xff0c…

《花雕学AI》36:探索Aski AI——集成问答、写作和绘画功能的强大AI平台

引言&#xff1a;人工智能是当今时代的最热门和最有前途的技术之一&#xff0c;它可以帮助人类解决各种复杂和有趣的问题&#xff0c;提高生活和工作的效率和质量。然而&#xff0c;人工智能的应用还面临着许多挑战和局限&#xff0c;比如数据的稀缺和质量、算法的复杂性和可解…