Vue+SpringBoot实现仿网盘项目

目录

一、效果展示

二、前端代码

三、后端代码及核心解释

四、进阶开发与思路


一、效果展示

1.1读取文件夹内的文件

1.2删除功能

1.3 上传文件

1.4 文件下载

对应的网盘实际地址与对应下载内容:


二、前端代码

2.1 创建vue项目(需要有vuex与router)并引入elementUi

npm i element-ui -S

2.2设置 VUEX(index.js):

import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)// store/index.js
export default new Vuex.Store({state: {selectedFiles: []},mutations: {ADD_TO_SELECTED(state, fileName) {state.selectedFiles.push(fileName);},REMOVE_FROM_SELECTED(state, fileName) {const index = state.selectedFiles.indexOf(fileName);if (index !== -1) {state.selectedFiles.splice(index, 1);}},REMOVE_ALL(state) {state.selectedFiles = [];}},// ...
});

组件:FileCard Component:

<template><div class="file-cards" style="line-height: normal;"><div v-for="(file, index) in fileList" :key="index" class="file-card" @click="toggleControlsAndSelect(index)"><i :class="[file.isDir ? 'el-icon-folder-opened' : 'el-icon-document', 'file-icon']"></i><div class="file-name">{{ file.name }}</div><!-- 添加勾选框 --><el-checkbox-group v-model="selectedFiles" @change="handleGroupChange"><el-checkbox :label="file.name" class="checkbox"></el-checkbox></el-checkbox-group></div></div>
</template><script>
import { mapState, mapMutations } from 'vuex';export default {props: {fileList: {type: Array,required: true,},},computed:{...mapState(['selectedFiles']),},data() {return {// selectedFiles: [], // 用于存储被选中的文件名};},methods: {...mapMutations(['ADD_TO_SELECTED', 'REMOVE_FROM_SELECTED']),handleGroupChange(values) {values.forEach(value => this.ADD_TO_SELECTED(value));this.fileList.filter(file => !values.includes(file.name)).forEach(file =>this.REMOVE_FROM_SELECTED(file.name));},toggleControlsAndSelect(index) {const fileName = this.fileList[index].name;if (this.selectedFiles.includes(fileName)) {this.REMOVE_FROM_SELECTED(fileName);} else {this.ADD_TO_SELECTED(fileName);}},},
};
</script><style scoped>
.file-cards {display: flex;flex-wrap: wrap;justify-content: center;
}.file-card {background-color: #fff;border-radius: 4px;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);margin: 10px;padding: 20px;width: 200px;cursor: pointer;transition: all 0.3s;
}.file-card:hover {box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.12);
}.file-icon {font-size: 50px;color: #409eff;
}.file-name {text-align: center;margin-top: 10px;
}
</style>

根组件:App.vue

Html:

<template><div id="Pan" style="border: 1px solid black; min-height: 90%; background-color: rgb(250, 250, 250);"><!-- 操作板块 --><div id="Operate"style="height: 50px; line-height: normal; border-top: 1px solid rgb(250, 250, 250); margin-top: 25px; "><el-upload class="upload-demo" action="/api/file/upload" :on-change="handleChange" :file-list="fileList" :show-file-list="showFileList"style=" display: inline-block;"><el-button type="primary"><i class="el-icon-upload2"></i>上传</el-button></el-upload><el-button type="success" style="margin-left: 10px;" @click="downloadSelectedFiles"><i class="el-icon-download"></i>下载</el-button><el-button type="primary" plain><i class="el-icon-share"></i>分享</el-button><el-button type="danger" plain @click="deleteFile"><i class="el-icon-delete"></i>删除</el-button></div><!-- 导航板块 --><div id="navigation"><el-breadcrumb separator-class="el-icon-arrow-right" style="padding-left: 10%; line-height: normal;"><el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item><el-breadcrumb-item :to="{ path: '/' }">我的网盘</el-breadcrumb-item></el-breadcrumb></div><!-- 全选文件 --><div style="height: 35px; background-color: white; border: 1px solid rgb(230, 230, 230);"><el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange"style="float: left; line-height: 35px; padding-left: 20%;">全选文件</el-checkbox></div><div id="FileList"><file-cards :file-list="sampleFiles" @update-file-selection="handleFileSelectionUpdate"></file-cards></div></div>
</template>


Javascript:

<script>
import FileCards from '@/components/FileCards.vue';
import Cookies from 'js-cookie';
import { mapState, mapMutations } from 'vuex';
import axios from 'axios';export default {computed: {...mapState(['selectedFiles']),},components: {FileCards,},data() {return {sampleFiles: [],// 文件全选isIndeterminate: false,checkAll: false,fileList: [],showFileList:false,};},methods: {...mapMutations(['ADD_TO_SELECTED', 'REMOVE_FROM_SELECTED', 'SET_ALL_SELECTED', 'REMOVE_ALL']),ListUserFiles() {const id = Cookies.get("userId");if (id === null) {this.$notify({title: '警告',message: '请还未登录,无法使用本功能',type: 'warning'});return;}},addToSelected(fileName) {if (!this.selectedFiles.includes(fileName)) {this.selectedFiles.push(fileName);}},removeFromSelected(fileName) {const index = this.selectedFiles.indexOf(fileName);if (index !== -1) {this.selectedFiles.splice(index, 1);}},handleFileSelectionUpdate(fileName, isChecked) {if (isChecked) {this.addToSelected(fileName);} else {this.removeFromSelected(fileName);}},// 全选文件handleCheckAllChange() {if (this.selectedFiles.length === this.sampleFiles.length) {this.REMOVE_ALL();return;}this.REMOVE_ALL();this.sampleFiles.forEach(file => {this.ADD_TO_SELECTED(file.name);});},// 上传文件handleChange() {console.log(this.fileList);this.$message.success('上传成功');this.getPanlist();},// 获取网盘文件列表getPanlist() {axios.get('api/file/list').then((Response) => {this.sampleFiles = Response.data.data;})},// 删除文件deleteFile() {axios.delete('api/file', {data: {fileNames: this.selectedFiles}}).then((response) => {this.REMOVE_ALL();this.getPanlist();console.log(response);});},downloadSelectedFiles() {console.log(this.selectedFiles);// 确保有文件被选中if (this.selectedFiles.length === 0) {alert("请选择要下载的文件!");return;}axios({url: 'api/file/download',method: 'POST',responseType: 'blob', // 告诉axios我们希望接收的数据类型是二进制流data: {fileNames: this.selectedFiles}}).then(response => {// 创建一个a标签用于触发下载let url = window.URL.createObjectURL(new Blob([response.data]));let link = document.createElement('a');link.href = url;// 如果你知道文件名,可以设置下载文件名link.setAttribute('download', 'download.zip');document.body.appendChild(link);link.click();// 清理document.body.removeChild(link);});},},mounted() {this.ListUserFiles();this.getPanlist();},};
</script>

css:

<style scoped>
#FileList {margin-top: 20px;
}#upload {float: left;
}
</style>

三、后端代码及核心解释

额外的依赖:

        <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency>

用以构造

3.1 返回类

//结果类
public class Result<T> {// 状态码常量public static final int SUCCESS = 200;public static final int ERROR = 500;private int code; // 状态码private String message; // 消息private T data; // 数据// 构造函数,用于创建成功的结果对象private Result(int code, String message, T data) {this.code = code;this.message = message;this.data = data;}// 成功结果的静态方法public static <T> Result<T> success(T data) {return new Result<>(SUCCESS, "Success", data);}// 错误结果的静态方法public static <T> Result<T> error(String message) {return new Result<>(ERROR, message, null);}// 错误结果的静态方法,可以传入自定义的状态码public static <T> Result<T> error(int code, String message) {return new Result<>(code, message, null);}// 获取状态码public int getCode() {return code;}// 设置状态码public void setCode(int code) {this.code = code;}// 获取消息public String getMessage() {return message;}// 设置消息public void setMessage(String message) {this.message = message;}// 获取数据public T getData() {return data;}// 设置数据public void setData(T data) {this.data = data;}// 用于转换为Map类型的方法,方便序列化为JSONpublic Map<String, Object> toMap() {Map<String, Object> map = new HashMap<>();map.put("code", code);map.put("message", message);map.put("data", data);return map;}
}

规范化后端返回Response的数据

由于本次上传都是小文件,后端限制在10MB以内.

@Configuration
public class servletMultipartConfigElement {@Beanpublic javax.servlet.MultipartConfigElement multipartConfigElement() {MultipartConfigFactory factory = new MultipartConfigFactory();// 设置单个文件的最大大小factory.setMaxFileSize(DataSize.ofMegabytes(10));// 设置整个请求的最大大小factory.setMaxRequestSize(DataSize.ofMegabytes(100));return factory.createMultipartConfig();}
}

3.2 获取用户的文件内容

    // 获取文件内容@GetMapping("/list")public Result getListByUserId() {//        TODO:后期以JWT鉴权方式,获取Token中的USerIDint id = 8;File directory = new File("E:\\ProjectReal\\AI WIth WEB SHell\\Pan" + File.separator + id);if (!directory.exists()) {boolean mkdirs = directory.mkdirs();if (mkdirs){Result.success("网盘创建成功");}else {Result.error("网盘创建失败");}return Result.error("异常");}// 直接将 fileList 转换为 JSONArrayJSONArray jsonArray = new JSONArray();File[] files = directory.listFiles();if (files != null) {for (File file : files) {JSONObject fileObj = new JSONObject();fileObj.put("name", file.getName());fileObj.put("isDir", file.isDirectory());fileObj.put("selected", false);jsonArray.add(fileObj);}}return Result.success(jsonArray);}

关键点在于通过java的IO与fastjson依赖构造出对应的JSON格式并返回

3.3 下载功能

@PostMapping("/download")public ResponseEntity<?> downloadSelectedFiles(@RequestBody FileNamesDto fileNamesDto) throws IOException {List<String> fileNames = fileNamesDto.getFileNames();ByteArrayOutputStream baos = new ByteArrayOutputStream();ZipOutputStream zos = new ZipOutputStream(baos);for (String fileName : fileNames) {File file = new File("E:\\ProjectReal\\AI WIth WEB SHell\\Pan" + File.separator + "8" + File.separator + fileName);if (file.exists()) {try (FileInputStream fis = new FileInputStream(file)) {ZipEntry zipEntry = new ZipEntry(fileName);zos.putNextEntry(zipEntry);byte[] bytes = new byte[1024];int length;while ((length = fis.read(bytes)) >= 0) {zos.write(bytes, 0, length);}zos.closeEntry();}catch (Exception e){e.printStackTrace();}}}zos.close();HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);headers.setContentDispositionFormData("attachment", "download.zip");return new ResponseEntity<>(baos.toByteArray(), headers, HttpStatus.OK);}

关键点在于,下载时候,不同的文件对应的请求头的MIME是不一样的,所以将文件先压缩后下载时候就只有一个文件格式为zip格式。

 3.4 删除功能

@DeleteMapping()public Result deleteFile(@RequestBody FileNamesDto fileNamesDto) {List<String> fileNames = fileNamesDto.getFileNames();int id = 8;for (String fileName : fileNames) {File file = new File("E:\\ProjectReal\\AI WIth WEB SHell\\Pan" + File.separator + id + File.separator + fileName);if (!file.exists()) {return Result.error("文件不存在");}if (file.isDirectory()){deleteDirectory(file);}else {boolean delete = file.delete();}}return Result.success("删除完成");}
    public static void deleteDirectory(File directory) {if (directory.exists()) {File[] entries = directory.listFiles();if (entries != null) {for (File entry : entries) {if (entry.isDirectory()) {deleteDirectory(entry);} else {entry.delete();}}}}directory.delete();}

注意:对于非空的directory是无法直接进行删除的,所以通过isDir判断如果是目录时候,则进行递归删除。将所有子文件都删除后再对目录进行删除.

3.5 上传功能

 @PostMapping("/upload")public ResponseEntity<?> upload(@RequestParam("file") MultipartFile file) {try {// 检查文件是否为空if (file.isEmpty()) {return ResponseEntity.badRequest().body("文件为空");}// 获取上传文件的原始文件名String originalFileName = file.getOriginalFilename();// 创建目录(如果不存在)File directory = new File("E:\\ProjectReal\\AI WIth WEB SHell\\Pan\\8");if (!directory.exists()) {directory.mkdirs();}// 文件保存路径Path targetLocation = Path.of(directory.getAbsolutePath(), originalFileName);try (InputStream inputStream = file.getInputStream()) {Files.copy(inputStream, targetLocation, StandardCopyOption.REPLACE_EXISTING);}return ResponseEntity.ok("上传成功");} catch (IOException e) {return ResponseEntity.status(500).body("上传失败:" + e.getMessage());}}

 由于前端上传的格式是multipartFIle 格式,所以后端也需要相应类型的进行接收对其进行接收


四、进阶开发与思路

4.1 前端

1.可以通过设置拖拽区域实现,当拖拽文件到网盘内容区时,自动执行上传函数的功能。

2.对于大文件,可以单独写一个对应的大文件上传页面,并展示上传进度条。

4.2 后端

1.大文件上传,首先前端进行判断文件的大小,如果超过一定的大小,则调用大文件上传功能。这时候就需要实现分片上传与断点续传功能。

2.云盘网站用户的独立性,这次演示的是一个固定用户的网盘内容。在实现真正项目时候,可以通过jwt鉴权的方式,获取token中的userId,使得获取到每一个用户自己的网盘。

3.云盘存量的设置,可以在遍历用户文件时候计算总大小,并返回给前端展示。


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

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

相关文章

C++笔试真题

可变分区管理方案 最佳适应&#xff1a;空闲区按容量递增最坏适应&#xff1a;空闲区按容量递减首先适应&#xff1a;空闲区按地址递增 C的结构体中有构造函数。 Linux新建用户或组 useradd&#xff1a;命令用于建立用户账号usermod&#xff1a;修改用户账号groupadd&#…

【模块化与包管理】:解锁【Python】编程的高效之道

目录 1.什么是模块&#xff1f; 2. 模块的导入过程 3. 理解命名空间 4. import语句的多种形式 5. 模块的执行与重新导入 6. 包&#xff08;Package&#xff09; 7. sys模块和os模块 sys模块 常用属性 示例&#xff1a;使用sys模块 os模块 常用功能 示例&#xff1…

【鸿蒙学习笔记】使用动画

官方文档&#xff1a;使用动画 目录标题 属性动画&#xff1a;通用属性发生改变时而产生的属性渐变效果animationanimateTo自定义属性动画 AnimatableExtend 转场动画&#xff1a;是页面或组件的切换动画 , 显示/隐藏 切换时的动画出现/消失转场&#xff1a;实现一个组件出现或…

【Superset】dashboard 自定义URL

URL设置 在发布仪表盘&#xff08;dashboard&#xff09;后&#xff0c;可以通过修改看板属性中的SLUG等&#xff0c;生成url 举例&#xff1a; http://localhost:8090/superset/dashboard/test/ 参数设置 以下 URL 参数可用于修改仪表板的呈现方式&#xff1a;此处参考了官…

SolidWorks滚花螺栓制作-cnblog

目标 规划基准图形 确定尺寸&#xff0c;单位mm 我 对固定好的图形进行旋转 倒角 设置螺纹 注意改变深度为15mm 收尾位置补全 滚花 建立基准面 制作多边形 添加穿透 扫描切除 圆周阵列 成品完成

【深度学习】手动完成线性回归!

&#x1f34a;嗨&#xff0c;大家好&#xff0c;我是小森( &#xfe61;ˆoˆ&#xfe61; )&#xff01; 易编橙终身成长社群创始团队嘉宾&#xff0c;橙似锦计划领衔成员、阿里云专家博主、腾讯云内容共创官、CSDN人工智能领域优质创作者 。 易编橙&#xff1a;一个帮助编程小…

现代码头装卸系统:技术创新与效率提升

引言 码头装卸系统在全球贸易和物流链中扮演着至关重要的角色。随着全球化进程的加快&#xff0c;国际贸易量不断增加&#xff0c;港口作为货物进出主要枢纽&#xff0c;其装卸效率直接影响到整个物流链的运作效率和成本。一个高效、现代化的码头装卸系统不仅能提高港口的货物处…

JVM是如何创建一个对象的?

哈喽&#xff0c;大家好&#x1f389;&#xff0c;我是世杰。 本文我为大家介绍面试官经常考察的**「Java对象创建流程」** 照例在开头留一些面试考察内容~~ 面试连环call Java对象创建的流程是什么样?JVM执行new关键字时都有哪些操作?JVM在频繁创建对象时&#xff0c;如何…

JVM垃圾回收器详解

垃圾回收器 JDK 默认垃圾收集器&#xff08;使用 java -XX:PrintCommandLineFlags -version 命令查看&#xff09;&#xff1a; JDK 8&#xff1a;Parallel Scavenge&#xff08;新生代&#xff09; Parallel Old&#xff08;老年代&#xff09; JDK 9 ~ JDK20: G1 堆内存中…

CVE-2024-6387Open SSH漏洞彻底解决举措(含踩坑内容)

一、漏洞名称 OpenSSH 远程代码执行漏洞(CVE-2024-6387) 二、漏洞概述 Open SSH是基于SSH协议的安全网络通信工具&#xff0c;广泛应用于远程服务器管理、加密文件传输、端口转发、远程控制等多个领域。近日被爆出存在一个远程代码执行漏洞&#xff0c;由于Open SSH服务器端…

2024年夏季德旺杯数学素养水平测试

此为小高组的测试&#xff0c;不过德旺杯主要看获奖情况&#xff0c;选择学员入营

基于考研题库小程序V2.0实现倒计时功能板块和超时判错功能

V2.0 需求沟通 需求分析 计时模块 3.1.1、功能描述←计时模块用于做题过程中对每一题的作答进行30秒倒计时&#xff0c;超时直接判错&#xff0c;同时将总用时显示在界面上;记录每次做题的总用时。 3.1.2、接口描述←与判定模块的接口为超时判定&#xff0c;若单题用时超过 …

人工智能和机器学习 (复旦大学计算机科学与技术实践工作站)20240703(上午场)人工智能初步、mind+人脸识别

前言 在这个科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经逐渐渗透到我们生活的方方面面&#xff0c;从智能家居到自动驾驶&#xff0c;无一不彰显着AI的强大潜力。而人脸识别技术作为AI领域的一项重要应用&#xff0c;更是以其高效、便捷的特点受到了…

萤石揽获2024葵花奖17项重磅大奖 登顶荣誉之巅

7月9日&#xff0c;第八届葵花奖智能家居评选颁奖盛典在中国建博会&#xff08;广州&#xff09;广交会展馆隆重举行。萤石共斩获横跨智能锁、智能家居摄像机、智能清洁、全屋智能以及物联网云平台等多个领域的17项大奖&#xff0c;创下行业最多记录&#xff0c;并问鼎金至尊奖…

记录|C#安装+HslCommunication安装

记录线索 前言一、C#安装1.社区版下载2.VS2022界面设置 二、HslCommunication安装1.前提2.安装3.相关文件【重点】 更新记录 前言 初心是为了下次到新的电脑上安装VS2022做C#上机位项目时能快速安装成功。 一、C#安装 1.社区版下载 Step1. 直接点击VS2022&#xff0c;跳转下…

华为机试HJ106字符逆序

华为机试HJ106字符逆序 题目&#xff1a; 想法&#xff1a; 将输入的字符串倒叙输出即可 input_str input()print(input_str[::-1])

二十年大数据到 AI,图灵奖得主眼中的数据库因果循环

最近&#xff0c;MIT 教授 Michael Stonebraker 和 CMU 教授 Andrew Pavlo (Andy) 教授联合发表了一篇数据库论文。Michael Stonebraker 80 高龄&#xff0c;是数据库行业唯一在世的图灵奖得主&#xff0c;Andy 则是业界少壮派里的最大 KOL。 一老一少&#xff0c;当今数据库届…

MVC架构

MVC架构 MVC架构在软件开发中通常指的是一种设计模式&#xff0c;它将应用程序分为三个主要组成部分&#xff1a;模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;和控制器&#xff08;Controller&#xff09;。这种分层结构有助于组织代码&#xff0c;使…

钡铼技术有限公司S270用于智慧物流中心货物追踪与调度

钡铼技术有限公司的第四代S270是一款专为智慧物流中心设计的工业级4G远程遥测终端RTU&#xff0c;其强大的功能和灵活性使其成为货物追踪与调度的理想选择。 技术规格和功能特点 钡铼S270支持多种通信协议&#xff0c;包括短信和MQTT&#xff0c;这使得它能够与各种云平台如华…

图论---匈牙利算法求二分图最大匹配的实现

开始编程前分析设计思路和程序的整体的框架&#xff0c;以及作为数学问题的性质&#xff1a; 程序流程图&#xff1a; 数学原理&#xff1a; 求解二分图最大匹配问题的算法&#xff0c;寻找一个边的子集&#xff0c;使得每个左部点都与右部点相连&#xff0c;并且没有两条边共享…