文章目录
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. 后续优化
- 新建文件夹优化【重名处理,新建命名】
- 多选,批量操作【下载复制删除】
- 右键文件实现操作栏展现