概述
本文要实现的功能比较简单:1、将想要共享的文件分文件夹的组织起来;2、别人可以通过界面进行搜索;3、可以在线预览或下载文件。基于这样的需求,本文分享通过node如何实现这样的功能。
实现效果
实现
1. 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. 当文件数量比较多的时候创建结构树效率比较低。
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-inputplaceholder="输入关键字进行过滤"v-model="filterText"clearable></el-input><el-treeclass="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 = thisif(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>