node快速搭建一个学习资料共享平台

2023-10-23 09:28:35 浏览数 (2)

概述

本文要实现的功能比较简单:1、将想要共享的文件分文件夹的组织起来;2、别人可以通过界面进行搜索;3、可以在线预览或下载文件。基于这样的需求,本文分享通过node如何实现这样的功能。

实现效果

实现

1. node端服务

node端服务通过express实现,并通过递归,读取目录下的所有文件。实现代码如下:

代码语言:javascript复制
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 的实现代码如下:

代码语言:javascript复制
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. 前端页面

前端页面简单使用VueElement实现,实现代码如下:

代码语言:javascript复制
<!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>

0 人点赞