基本实现了首页文件列表,完善了上传和下载功能
This commit is contained in:
parent
dce08a43a4
commit
c1a5437532
@ -2,8 +2,8 @@ require('dotenv').config()
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
SERVER_HOST: process.env.HOST || 'localhost',
|
SERVER_HOST: process.env.HOST || 'localhost',
|
||||||
SERVER_PORT: process.env.PORT || '3000',
|
SERVER_PORT: process.env.PORT || '3000',
|
||||||
MONGODB_URL: process.env.MONGDB || 'mongodb://localhost/data',
|
MONGODB_URL: process.env.MONGDB || 'mongodb://localhost/filesharing',
|
||||||
TEMP_PATH: '/tmp',
|
UPLOAD_SIZE_LIMIT: process.env.UPLOAD_SIZE_LIMIT || 102400,
|
||||||
SAVE_PATH: 'upload',
|
UPLOAD_TEMP_PATH: process.env.UPLOAD_TEMP_PATH || '/tmp',
|
||||||
UPLOAD_SIZE_LIMIT: 1024,
|
UPLOAD_SAVE_PATH: process.env.UPLOAD_SAVE_PATH || 'upload',
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ body {
|
|||||||
|
|
||||||
p,
|
p,
|
||||||
h1,
|
h1,
|
||||||
|
li,
|
||||||
form {
|
form {
|
||||||
margin: 1rem auto;
|
margin: 1rem auto;
|
||||||
}
|
}
|
||||||
@ -26,3 +27,8 @@ form button {
|
|||||||
grid-column: span 2;
|
grid-column: span 2;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.small {
|
||||||
|
color: gray;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
50
server.js
50
server.js
@ -1,4 +1,4 @@
|
|||||||
const { SERVER_HOST, SERVER_PORT, MONGODB_URL, TEMP_PATH, SAVE_PATH, UPLOAD_SIZE_LIMIT } = require('./config')
|
const { SERVER_HOST, SERVER_PORT, MONGODB_URL, UPLOAD_TEMP_PATH, UPLOAD_SAVE_PATH, UPLOAD_SIZE_LIMIT } = require('./config')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const multer = require('multer')
|
const multer = require('multer')
|
||||||
@ -6,7 +6,7 @@ const bcrypt = require('bcrypt')
|
|||||||
const md5file = require('md5-file')
|
const md5file = require('md5-file')
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
const mongoose = require('mongoose')
|
const mongoose = require('mongoose')
|
||||||
const File = require('./models/File')
|
const Filesharing = require('./models/Filesharing')
|
||||||
mongoose.connect(MONGODB_URL, (error) => {
|
mongoose.connect(MONGODB_URL, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@ -19,19 +19,26 @@ app.set('view engine', 'pug')
|
|||||||
app.use(express.static('public'))
|
app.use(express.static('public'))
|
||||||
app.use(express.urlencoded({ extended: true }))
|
app.use(express.urlencoded({ extended: true }))
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
app.get('/', (req, res) => {
|
app.locals.moment = require('moment')
|
||||||
|
app.locals.moment.locale('zh-cn')
|
||||||
|
app.get('/', async (req, res) => {
|
||||||
|
const filesharing = await Filesharing.find().sort({ createdAt: -1 })
|
||||||
|
res.render('index', { filesharing })
|
||||||
|
})
|
||||||
|
app.get('/upload', async (req, res) => {
|
||||||
res.render('upload')
|
res.render('upload')
|
||||||
})
|
})
|
||||||
const upload = multer({ dest: TEMP_PATH, limits: { fileSize: UPLOAD_SIZE_LIMIT } })
|
const upload = multer({ dest: UPLOAD_TEMP_PATH, limits: { fileSize: UPLOAD_SIZE_LIMIT } })
|
||||||
app.post('/upload', upload.single('file'), async (req, res) => {
|
app.post('/upload', upload.single('file'), async (req, res) => {
|
||||||
const file_temp_path = path.join(TEMP_PATH, req.file.filename)
|
const file_temp_path = path.join(UPLOAD_TEMP_PATH, req.file.filename)
|
||||||
const md5 = await md5file(file_temp_path)
|
const md5 = await md5file(file_temp_path)
|
||||||
const file_save_path = path.join(__dirname, SAVE_PATH, md5)
|
const file_save_path = path.join(__dirname, UPLOAD_SAVE_PATH, md5)
|
||||||
if (!fs.existsSync(file_save_path)) {
|
if (!fs.existsSync(file_save_path)) {
|
||||||
fs.cpSync(file_temp_path, file_save_path) // 复制临时文件到UPLOAD_PATH
|
fs.cpSync(file_temp_path, file_save_path) // 复制临时文件到UPLOAD_PATH
|
||||||
}
|
}
|
||||||
fs.unlinkSync(file_temp_path) // 删除临时文件
|
fs.unlinkSync(file_temp_path) // 删除临时文件
|
||||||
let file = {
|
// 写入数据库
|
||||||
|
const filesharing = new Filesharing({
|
||||||
md5,
|
md5,
|
||||||
size: req.file.size,
|
size: req.file.size,
|
||||||
encoding: req.file.encoding,
|
encoding: req.file.encoding,
|
||||||
@ -39,33 +46,34 @@ app.post('/upload', upload.single('file'), async (req, res) => {
|
|||||||
// 解决了multer将中文文件名错误编码的问题
|
// 解决了multer将中文文件名错误编码的问题
|
||||||
filename: Buffer.from(req.file.originalname, 'latin1').toString('utf8'),
|
filename: Buffer.from(req.file.originalname, 'latin1').toString('utf8'),
|
||||||
password: req.body.password ? await bcrypt.hash(req.body.password, 16) : '',
|
password: req.body.password ? await bcrypt.hash(req.body.password, 16) : '',
|
||||||
}
|
})
|
||||||
// 写入数据库
|
const file = await filesharing.save()
|
||||||
console.log(req.file)
|
|
||||||
console.log(file)
|
|
||||||
const fileshare = new File(file)
|
|
||||||
file = await fileshare.save()
|
|
||||||
console.log(file)
|
|
||||||
res.status(201).render('upload', { file })
|
res.status(201).render('upload', { file })
|
||||||
})
|
})
|
||||||
app.get('/file/:id', async (req, res) => {
|
app.get('/file/:id', async (req, res) => {
|
||||||
const file = await File.findById(req.params.id)
|
try {
|
||||||
if (file) {
|
if (!req.params.id.match(/^[0-9a-fA-F]{24}$/)) throw '格式错误的ObjectId'
|
||||||
|
const file = await Filesharing.findById(req.params.id)
|
||||||
|
if (!file) throw '试图下载不存在的文件'
|
||||||
res.render('download', { file })
|
res.render('download', { file })
|
||||||
} else {
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
res.sendStatus(404)
|
res.sendStatus(404)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
app.post('/file/:id', async (req, res) => {
|
app.post('/file/:id', async (req, res) => {
|
||||||
const file = await File.findById(req.params.id)
|
try {
|
||||||
if (file) {
|
if (!req.params.id.match(/^[0-9a-fA-F]{24}$/)) throw '格式错误的ObjectId'
|
||||||
|
const file = await Filesharing.findById(req.params.id)
|
||||||
|
if (!file) throw '试图下载不存在的文件'
|
||||||
if (file.password == '' || (await bcrypt.compare(req.body.password, file.password)) == true) {
|
if (file.password == '' || (await bcrypt.compare(req.body.password, file.password)) == true) {
|
||||||
await file.update({ $inc: { downloads: 1 } })
|
await file.update({ $inc: { downloads: 1 } })
|
||||||
res.status(200).download(path.join(SAVE_PATH, file.md5), file.filename)
|
res.status(200).download(path.join(UPLOAD_SAVE_PATH, file.md5), file.filename)
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(401)
|
res.sendStatus(401)
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
res.sendStatus(404)
|
res.sendStatus(404)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -6,22 +6,25 @@ html(lang='zh')
|
|||||||
meta(name='viewport' content='width=device-width, initial-scale=1.0')
|
meta(name='viewport' content='width=device-width, initial-scale=1.0')
|
||||||
link(rel='icon' href='/favicon.ico' type='image/x-icon')
|
link(rel='icon' href='/favicon.ico' type='image/x-icon')
|
||||||
link(rel='stylesheet' href='/style.css')
|
link(rel='stylesheet' href='/style.css')
|
||||||
title 文件下载
|
title #{file.filename} | 文件下载
|
||||||
body
|
body
|
||||||
h1 文件下载
|
h1 文件下载
|
||||||
|
p
|
||||||
|
a(href='/') 返回首页
|
||||||
form(method='POST')
|
form(method='POST')
|
||||||
label 文件
|
label 文件
|
||||||
label= file.filename
|
label= file.filename
|
||||||
|
label 类型
|
||||||
|
label= file.mimetype
|
||||||
label 大小
|
label 大小
|
||||||
label= file.size
|
label= file.size
|
||||||
| 字节
|
| 字节
|
||||||
label 类型
|
label 上传
|
||||||
label= file.mimetype
|
label= moment(file.createdAt).fromNow()
|
||||||
label 下载
|
label 热度
|
||||||
label= file.downloads
|
label= file.downloads
|
||||||
| 次
|
| 次下载
|
||||||
if file.password
|
if file.password
|
||||||
label(for='password_input') 密码
|
label(for='password_input') 密码
|
||||||
input(id='password_input' name='password' type='password' autocomplete='off' required)
|
input(id='password_input' name='password' type='password' autocomplete='off' required)
|
||||||
button(id='download_button' type='submit') 下载
|
button(id='download_button' type='submit') 下载
|
||||||
script(src='/client.js')
|
|
||||||
|
23
views/index.pug
Normal file
23
views/index.pug
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
doctype html
|
||||||
|
html(lang='zh')
|
||||||
|
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')
|
||||||
|
link(rel='icon' href='/favicon.ico' type='image/x-icon')
|
||||||
|
link(rel='stylesheet' href='/style.css')
|
||||||
|
title 文件分享
|
||||||
|
body
|
||||||
|
h1 文件分享
|
||||||
|
p
|
||||||
|
a(href='/upload') 我要上传
|
||||||
|
ul
|
||||||
|
if filesharing.length==0
|
||||||
|
li 暂无文件
|
||||||
|
else
|
||||||
|
for file in filesharing
|
||||||
|
li
|
||||||
|
a(href=`/file/${file.id}` target='_blank')= file.filename
|
||||||
|
if file.password
|
||||||
|
span ㊙️
|
||||||
|
span.small ( #{moment(file.createdAt).fromNow()}, #{file.downloads} 次下载 )
|
@ -6,9 +6,11 @@ html(lang='zh')
|
|||||||
meta(name='viewport' content='width=device-width, initial-scale=1.0')
|
meta(name='viewport' content='width=device-width, initial-scale=1.0')
|
||||||
link(rel='icon' href='/favicon.ico' type='image/x-icon')
|
link(rel='icon' href='/favicon.ico' type='image/x-icon')
|
||||||
link(rel='stylesheet' href='/style.css')
|
link(rel='stylesheet' href='/style.css')
|
||||||
title 文件分享
|
title 文件上传
|
||||||
body
|
body
|
||||||
h1 文件分享
|
h1 文件上传
|
||||||
|
p
|
||||||
|
a(href='/') 返回首页
|
||||||
if file
|
if file
|
||||||
section
|
section
|
||||||
p
|
p
|
||||||
@ -16,7 +18,7 @@ html(lang='zh')
|
|||||||
var #{file.filename}
|
var #{file.filename}
|
||||||
| 分享成功!
|
| 分享成功!
|
||||||
p 链接
|
p 链接
|
||||||
-const url = '/file/' + file.id
|
- const url = '/file/' + file.id
|
||||||
a(href=url)= url
|
a(href=url)= url
|
||||||
form(method='POST' action='/upload' enctype='multipart/form-data')
|
form(method='POST' action='/upload' enctype='multipart/form-data')
|
||||||
label(for='file_input') 文件
|
label(for='file_input') 文件
|
||||||
@ -24,4 +26,3 @@ html(lang='zh')
|
|||||||
label(for='password_input') 密码
|
label(for='password_input') 密码
|
||||||
input(id='password_input' name='password' type='password' autocomplete='off')
|
input(id='password_input' name='password' type='password' autocomplete='off')
|
||||||
button(id='share_button' type='submit') 分享
|
button(id='share_button' type='submit') 分享
|
||||||
script(src='/client.js')
|
|
||||||
|
Loading…
Reference in New Issue
Block a user