本文要实现的功能比较简单:1、将想要共享的文件分文件夹的组织起来;2、别人可以通过界面进行搜索;3、可以在线预览或下载文件。基于这样的需求,本文分享通过node如何实现这样的功能。
node端服务通过express
实现,并通过递归,读取目录下的所有文件。实现代码如下:
const express = require('express');
const app = express();
const { listFiles } = require('./utils/file');
app.use(express.static('./www'));
app.get('/files', function (req, res) {
res.send({
code: 200,
data: listFiles('./www/')
});
});
app.listen(18888, () => {
console.log('running at http://localhost:18888');
})
listFiles
的实现代码如下:
function listFiles(path, rootPath = "") {
const items = fs.readdirSync(path);
const result = [];
items.forEach((item) => {
const itemPath = `${path}/${item}`;
const stat = fs.statSync(itemPath);
if (stat.isDirectory()) {
let data = {
// 文件夹
type: "folder",
name: item,
};
let children = listFiles(
itemPath,
rootPath ? `${rootPath}/${item}` : item
);
if (children && children.length) {
data.children = children;
}
result.push(data);
} else {
// 文件
if (item.indexOf("index.html") === -1) {
result.push({
type: "file",
name: item,
url: rootPath ? `${rootPath}/${item}` : item,
});
}
}
});
return result;
}
返回后的数据格式如下:
思考:这样的方式, 1. 文件上传不太方便,可以用网盘;2. 当文件数量比较多的时候创建结构树效率比较低。
前端页面简单使用Vue
和Element
实现,实现代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>学习资料</title>
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入组件库 -->
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
body {
font-size: 14px;
overflow: hidden;
}
h1 {
padding: 1rem;
background-color: #0062ff;
color: white;
font-size: 1.2rem;
}
.file-tree {
padding: 1rem;
}
.filter-tree {
margin-top: 1rem;
height: calc(100vh - 9rem);
overflow-y: auto;
}
.el-tree-node__content {
height: 2.4rem;
}
.tree-text {
font-size: 0.98rem;
}
.slot-t-node-file:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div id="app" class="container">
<h1>学习资料</h1>
<div class="file-tree">
<el-input
placeholder="输入关键字进行过滤"
v-model="filterText"
clearable
></el-input>
<el-tree
class="filter-tree"
:data="filteredFileData"
default-expand-all
@node-click="handleNodeClick"
ref="tree"
>
<span slot-scope="{ node, data }" class="slot-t-node" :class="data.type === 'file' ? 'slot-t-node-file' : ''">
<template>
<i :class="getIcon(node, data)" class="tree-text"></i>
<span class="tree-text">{{ data.name }}</span>
</template>
</span>
</el-tree>
</div>
</div>
<script>
const app = new Vue({
el: "#app",
mounted() {
this.getFileList();
},
computed: {
filteredFileData() {
const that = this
if(that.filterText === '') return that.fileData;
let filter = function (data) {
let result = []
data.forEach(d => {
if(d.children) {
const res = filter(d.children)
if(res.length > 0) result.push({...d, children: res})
} else {
if(d.name.toLowerCase().indexOf(that.filterText.toLowerCase()) !== -1) result.push(d)
}
})
return result
}
return filter(that.fileData)
},
},
data() {
return {
filterText: "",
fileData: [],
};
},
methods: {
getIcon(node, data) {
if (data.type === "file") {
return "el-icon-document";
} else {
return node.expanded ? "el-icon-folder-opened" : "el-icon-folder";
}
},
getFileList() {
const url = `/files`;
fetch(url)
.then((res) => res.json())
.then((res) => {
this.fileData = res.data;
});
},
handleNodeClick(data, node, component) {
const { url } = data;
if (url) window.open(url, "_blank");
},
},
});
</script>
</body>
</html>