效果图:
HTML源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件共享</title>
<style>
.title{
text-align: center;
}
.table{
width: 100%;
}
.thead{
background-color: #ccc;
}
th{
padding: 5px 0;
}
td{
text-align: center;
border-bottom: 1px dashed #999;
padding: 3px 0;
}
.btn{
color: #409eff;
cursor: pointer;
}
.top{
display: flex;
align-items: center;
justify-content: space-between;
padding: 5px 10px;
}
#list > tr:hover{
background-color: #eee;
}
</style>
</head>
<body>
<h1 class="title">欢迎使用文件共享</h1>
<div class="top">
<span id="nav">/</span>
<div>
<span class="btn" id="backUp">返回上一级</span>
<span>共<span id="total">0</span>条</span>
</div>
</div>
<table class="table">
<thead class="thead">
<tr>
<th>文件名</th>
<th>类型</th>
<th>大小</th>
<th>创建时间</th>
<th>下载</th>
</tr>
</thead>
<tbody id="list">
</tbody>
</table>
<script>
const listEl = document.querySelector('#list')
const totalEl = document.querySelector('#total')
const navEl = document.querySelector('#nav')
const backUp = document.querySelector('#backUp')
const nevList = []
listEl.addEventListener('click',handleDownload)
async function getList(query = ''){
let result = ''
if(query) {
result = await fetch(`/list?pathName=${query}`)
navEl.innerHTML = query
}else{
result = await fetch('/list')
navEl.innerHTML = '/'
}
const {data} = await result.json()
const elArr = []
data.forEach(item => {
elArr.push(`
<tr>
<td>${item.name}</td>
<td>${item.type === 1 ?'文件' : '文件夹'}</td>
<td>${item.size}</td>
<td>${item.createTime}</td>
<td class="btn"
fileName="${item.name}"
fileType="${item.type}"
path="${item.path}">${item.type === 1 ? '下载' : '查看'}</td>
</tr>
`)
})
listEl.innerHTML = elArr.join('')
totalEl.innerHTML = data.length
}
getList()
function handleDownload(e){
const fileName = e.target.attributes.filename.value
const fileType = e.target.attributes.fileType.value
const path = e.target.attributes.path.value
if(!fileName) return
if(fileType == 1) {
window.open(path)
}else{
getList(path)
nevList.push(path)
}
}
backUp.addEventListener('click',(e)=>{
if(!nevList.length) return
if(nevList.length < 2) {
nevList.shift()
getList('')
}else{
nevList.pop()
getList(nevList[nevList.length - 1])
}
})
</script>
</body>
</html>
Node 源码
const express = require('express')
const path = require('path')
const fs = require('fs')
const app = express()
// 常量
const pagePath = __dirname + '/index.html'
const staticPath = __dirname + '/static'
app.use(express.static('static'))
app.get('/', (req, res) => {
res.sendFile(pagePath)
})
app.get('/list', (req, res) => {
const { pathName } = req.query
const filePath = pathName ? staticPath + `/${pathName}` : staticPath
const result = []
const allFiles = fs.readdirSync(filePath)
allFiles.forEach(name => {
const fileInfo = fs.statSync(`${filePath}/${name}`)
result.push({
name,
path: pathName ? `${pathName}/${name}` : `/${name}`,
size: computedSize(fileInfo.size),
type: fileInfo.isFile() ? 1 : 0,
createTime: fileInfo.ctime.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }),
})
})
res.json({
data: result,
})
})
function computedSize(size) {
const kb = size / 1024
const mb = kb / 1024
return mb.toFixed(2) + ' MB'
}
app.listen(9000, () => {
console.log('server running port:9000')
})
目录层级: