【文件系统】Vue+SpringBoot模拟文件系统(2)

0. 知识预告

  • Vue

  • Element

  • Vuex

  • localStorage

  • SpringBoot

1. 实时更新当前文件(夹)位置

Vuex + LocalStorge

  • 在store的index.js中(vuex)
  • vuex便于全局访问
  • localStorage通过vue把变量写入到浏览器cookie,保证刷新后仍在当前目录位置
  • 思路大概是找到当前是第几个文件夹,切分原来的curUrl,然后只取前 i 个文件夹名称重组并覆盖
state: {
    //状态变量记得分级,代码可读性高点,也便于扩展
	file: {
		//表示先从浏览器的缓存找,如果没有,就默认赋值为null,如果有就直接取
		curUrl: window.localStorage.getItem("curUrl")===null?"":window.localStorage.getItem("curUrl")
	}
},
mutations: {
    //设定方法,参数是一个json对象,用来获取多个参数
	modifyCurUrl(state, {rowName, dir}) {
        //-1表示处理 进入下一个文件夹 的功能
        if(dir == -1) {
        	state.file.curUrl = state.file.curUrl + "\\" + rowName;
        }else if(dir == -2){
            //-2表示 处理 返回上一级 的功能
            //就是一个字符串处理,把最后一个\\往后的子串删除
            state.file.curUrl = state.file.curUrl.substring(0, state.file.curUrl.lastIndexOf('\\'));
        }else {
            //如果不是上面两个请求,则说明现在是点击面包屑主动跳转
            //传递当前要跳转的文件夹名称,dir现在有意义了,表示第几个文件夹
            //切分curUrl,找到第 dir 个文件夹名称,然后重组url,改变状态变量和浏览器cookie
            var list = state.file.curUrl.split("\\").filter(item=>item)
            var url = ""
            for(var i in list) {
                if(dir != -1) {
                    url += "\\" 
                    url += list[i]
                    dir -= 1
                }
            }
            //修改状态变量
        	state.file.curUrl = url
        }
		
        //无论如何,只要涉及到curUrl改变,就在浏览器cookie中覆盖掉原来的值,这样前端就能一直获取到正确的路径了
    	window.localStorage.setItem('curUrl', state.file.curUrl)
    }
}

2. 实现双击进入文件夹

因为是后续加的功能,所以前端结构已经被优化过了

后端已经做过介绍了,就不写了,使用的还是showAllFile接口。

2.1 前端

  • 利用记录当前文件夹位置的功能

  • 给el-table的row-dblclick双击事件绑定一个响应方法,就是当前的showAllFile

<el-table @row-dblclick="showAllFile">
  • 根据elementUI官方文档可以知道,该事件会默认传递row, column, event三个参数。我们主需要row参数去获取当前行对象即可
showAllFile(row) {
    //如果是文件夹,才允许响应双击事件,如果是文件直接跳过不响应
    if(row.isDir == 1) {
        //把当前要交进入的文件夹的名字发送过去,dir=-1表示我只要响应进入下一级文件的处理
        this.$store.commit("modifyCurUrl", {rowName:row.name, dir:-1});
        this.$http.post("/file/showAllFiles", {
            curUrl: this.$store.state.file.curUrl, 
            uid: this.$store.state.user.uid
        }).then((res) => {
            //覆盖table数据
        	this.tableData = res.data;
        });
    }
},

2.2 效果展示:

在这里插入图片描述

双击 d 文件夹后再双击 c 文件夹

在这里插入图片描述

3. 面包屑跳转实现

Vue+Element+Vuex+localStorage

后端使用的还是showAllFile接口。

3.1 前端

  • 我们使用element的breadcrumb面包屑作为基础,对跳转方式做一个改写就好了
  • 思路大概是
    • 返回上一级 和 全部文件 属于固定item,不懂根据当前路径动态更改
    • 而要实时更新我们的面包屑,只需要读取我们实时变更的curUrl即可【上面说到showAllFile接口传递的】
    • 根据我们vuex中的分隔url思路,我们可以获取到n个文件夹名称,我们只需要通过v-for把每个名称都做成一个面包屑单位即可
vue:
<el-breadcrumb separator="/" style="cursor:pointer;padding-left: 15px">
    <el-breadcrumb-item class="bread"><a style="color: rgb(74 164 255);" @click="backUp">返回上一级</a></el-breadcrumb-item>
    <el-breadcrumb-item class="bread"><a style="color: rgb(74 164 255);" @click="all">全部文件</a></el-breadcrumb-item>
    <el-breadcrumb-item class="bread" v-for="(item, i) in this.$store.state.file.curUrl.split('\\').filter(item=>item)" :key="i">
        <a style="color: rgb(74 164 255);" @click="toDir(item, i)">{{ item }}</a>
    </el-breadcrumb-item>
</el-breadcrumb>

  • 下面是三个js函数
  • 详情看注释,部分具体处理返回 第一部分:实时更新当前文件夹位置 详细回顾
//返回上一级
backUp() {
    //根据上面vuex的介绍,传入-2就是表示只响应 返回上一级 的处理,字符串处理详情回顾第一part
    this.$store.commit('modifyCurUrl', {rowName: '', dir: -2})
    this.$http.post("/file/showAllFiles", {
        curUrl: this.$store.state.file.curUrl, 
        uid: this.$store.state.user.uid
    }).then((res) => {
        this.tableData = res.data;
    });
},
//跳转到特定目录
toDir(item, i) {
    //我们能获得索引i(v-for提供),也就是当前点击的是第i个面包屑单位,对应第i个文件夹,详细处理往上回顾
    this.$store.commit('modifyCurUrl', {rowName: item, dir: i})
    this.$http.post("/file/showAllFiles", {
        curUrl: this.$store.state.file.curUrl, 
        uid: this.$store.state.user.uid
    }).then((res) => {
        this.tableData = res.data;
    });

},
//点击全部文件
all() {
    //这是点击全部文件时候的直接跳转,所以要重置全局变量
    this.$store.state.file.curUrl = ""
    this.$store.commit("modifyCurUrl",{rowName: "" ,dir: 0})
    this.$http.post("/file/showAllFiles", {
          //直接展示根目录文件
          curUrl: "\\", 
          uid: this.$store.state.user.uid
    }).then((res) => {
          this.tableData = res.data;
    });
},

3.2 效果展示

在这里插入图片描述

4. 单个文件下载功能实现

4.1 前端展示列表优化

  • 使用el-table的template自定义功能

    • <template\ slot = “header”> 表示自定义表头,添加更多元素而不单单只是字符串
    这样可以对表头进行标签添加
    <template slot="header">
        <i class="el-icon-document hidden-xs-only"></i>
        <span>文件名</span>
    </template>
    
    • <template\ slot-scope=“scope”> 表示自定义列,该列数据内容会被自定义化,同时我们可以通过scope.row来获取整个行对象,scope.row.name表示当前行的name属性。
    • 如下,只需要在 操作 列放两个按钮即可,下载按钮绑定download(scope.row.name)函数
    <el-table-column prop="operation" label="操作" width="110">
        <template slot-scope="scope">
            <el-button type="text" icon="el-icon-download" @click="download(scope.row.name)"></el-button>
            <el-button type="text" icon="el-icon-close" @click="confirm"></el-button>
        </template>
    </el-table-column>
    

效果展示:

如图,表头文件名自定义了一个图标。文件名下方所有文件夹(给个判断即可)都增加了图标。操作也自定义了按钮。

在这里插入图片描述

  • 下载函数
    • 有很多调用浏览器下载功能的方法,可以用post发送请求blob二进制大对象。
    • 但是最方便的还是通过a标签的get方法直接访问后端对应文件,走其他路其实有点舍近求远。当然如果需要严格的安全验证,还是使用post方法发送token令牌获取。
    • 所以我们选择插入一个a标签元素并手动点击。
    • 注意要使用encodeURIComponent()去编码变量,否则url会出问题【直接传特殊符号或者中文会报错】。
download(name) {
    const a = document.createElement('a')
    var uid = this.$store.state.user.uid
    var curUrl = encodeURIComponent(this.$store.state.file.curUrl)
    var href = "/file/downloadFile?"+"uid="+uid+"&curUrl="+curUrl+"&name="+encodeURIComponent(name)
    a.setAttribute('href', href)
    // 下载文件名,如果后端没有返回,可以自己写a.download = '文件.pdf'
	a.download = filename
	document.body.appendChild(a)
	a.click()
	//下载完可以删掉a标签及url对象,节省网络内存
	document.body.removeChild(a)
	// 在内存中移除URL 对象
	window.URL.revokeObjectURL(herf)
}

4.2 后端

4.2.1 Controller

  • 我们通过Service把文件流写进response,不需要返回其他东西,浏览器会读取response对象并提供下载。
@GetMapping("/downloadFile")
public void downloadFile(HttpServletResponse response, String uid, String curUrl, String name) throws IOException {
    String localUrl = store + uid + curUrl + "\\" + name;
    fileService.downloadFile(response, localUrl, name);
}

4.2.2 Service

  • 开本地文件流
  • 读本地文件然后写进response对象
  • 需要注意,我们要设置一些response的Header和ContentType,否则浏览器可能会错误解析
@Override
public void downloadFile(HttpServletResponse response, String localUrl, String fileName)throws IOException {
    File file = new File(localUrl);
    if (file.exists()) {
        response.setHeader("content-type", "application/octet-stream");
        response.setContentType("application/octet-stream");
        // 下载文件能正常显示中文
        try {
            //注意要对中文进行URL编码,否则浏览器会乱码或者无法显示文件名
            //同时URLEncoder.encode()会把空格编码成+号,我们要手动替换成"%20",浏览器能解析成空格
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+","%20"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        // 实现文件下载
        byte[] buffer = new byte[1024];
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        try {
            fis = new FileInputStream(file);
            bis = new BufferedInputStream(fis);
            OutputStream os = response.getOutputStream();
            int i = bis.read(buffer);
            while (i != -1) {
                os.write(buffer, 0, i);
                i = bis.read(buffer);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            assert bis != null;
            bis.close();
            fis.close();
        }
    }
}

4.3 效果展示

在这里插入图片描述

5. 后续优化

  • 新建文件夹优化【重名处理,新建命名】
  • 多选,批量操作【下载复制删除】
  • 右键文件实现操作栏展现
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

玖等了

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

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

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

打赏作者

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

抵扣说明:

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

余额充值