基本实现了首页文件列表,完善了上传和下载功能

This commit is contained in:
赵鑫 2022-08-26 23:45:07 +08:00
parent dce08a43a4
commit c1a5437532
7 changed files with 79 additions and 36 deletions

View File

@ -1 +1,3 @@
# 文件分享服务器 # 文件分享
一个文件分享服务应用

View File

@ -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',
} }

View File

@ -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;
}

View File

@ -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)
} }
}) })

View File

@ -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
View 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} 次下载 )

View File

@ -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')