125 lines
5.0 KiB
JavaScript
125 lines
5.0 KiB
JavaScript
require('dotenv').config()
|
|
const HOST = process.env.HOST || 'localhost'
|
|
const PORT = process.env.PORT || '3000'
|
|
const MONGODB_URL = process.env.MONGODB_URL || 'mongodb+srv://test:It6E1HC5p5K83bS8@test.ekqr7.azure.mongodb.net/test?retryWrites=true&w=majority'
|
|
const UPLOAD_SIZE_LIMIT = process.env.UPLOAD_SIZE_LIMIT || 102400
|
|
const UPLOAD_TEMP_PATH = process.env.UPLOAD_TEMP_PATH || '/tmp'
|
|
const UPLOAD_SAVE_PATH = process.env.UPLOAD_SAVE_PATH || 'upload'
|
|
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
const md5file = require('md5-file')
|
|
const filesize = require('filesize')
|
|
const { hash, compare } = require('bcryptjs')
|
|
|
|
const mongoose = require('mongoose')
|
|
mongoose.connect(MONGODB_URL, { useNewUrlParser: true }).catch(error => console.error(error))
|
|
mongoose.connection.on('connected', () => console.info('mongodb is connected'))
|
|
mongoose.connection.on('error', (error) => console.error(error))
|
|
const Filesharing = require('./models/Filesharing')
|
|
|
|
const CronJob = require('cron').CronJob
|
|
const job = new CronJob(
|
|
'0 * * * * *',
|
|
async () => {
|
|
const files = await Filesharing.find({ createdAt: { $lt: new Date(Date.now() - 24 * 3600 * 1000) } })
|
|
for (let file of files) {
|
|
const id = file.id
|
|
await Filesharing.findByIdAndDelete(id)
|
|
console.log(`删除共享 ${id}`)
|
|
const ref_number = await Filesharing.countDocuments({ md5: file.md5 })
|
|
if (ref_number == 0) {
|
|
const file_path = path.join(__dirname, UPLOAD_SAVE_PATH, file.md5)
|
|
fs.unlinkSync(file_path)
|
|
console.log(`删除文件 ${file_path}`)
|
|
}
|
|
}
|
|
},
|
|
null,
|
|
true
|
|
)
|
|
|
|
const express = require('express')
|
|
const app = express()
|
|
app.use(express.static('public'))
|
|
app.set('view engine', 'pug')
|
|
app.use(express.urlencoded({ extended: false }))
|
|
app.use(express.json())
|
|
app.locals.moment = require('moment')
|
|
app.locals.moment.locale('zh-cn')
|
|
app.locals.filesize = filesize.partial({ base: 2, standard: 'jedec' })
|
|
app.get('/', async (req, res) => {
|
|
const files = await Filesharing.find().sort({ createdAt: -1 })
|
|
res.render('index', { title: '文件分享', files })
|
|
})
|
|
app.get('/upload', async (req, res) => {
|
|
res.render('upload', { title: '文件上传' })
|
|
})
|
|
|
|
const multer = require('multer')
|
|
const upload = multer({ dest: UPLOAD_TEMP_PATH, limits: { fileSize: UPLOAD_SIZE_LIMIT } })
|
|
app.post('/upload', upload.single('file'), async (req, res) => {
|
|
const file_temp_path = path.join(UPLOAD_TEMP_PATH, req.file.filename)
|
|
const md5 = await md5file(file_temp_path)
|
|
const file_save_path = path.join(__dirname, UPLOAD_SAVE_PATH, md5)
|
|
if (!fs.existsSync(file_save_path)) {
|
|
fs.cpSync(file_temp_path, file_save_path) // 复制临时文件到UPLOAD_PATH
|
|
}
|
|
fs.unlinkSync(file_temp_path) // 删除临时文件
|
|
// 写入数据库
|
|
const file = await Filesharing.create({
|
|
md5,
|
|
size: req.file.size,
|
|
encoding: req.file.encoding,
|
|
mimetype: req.file.mimetype,
|
|
// 解决了multer将中文文件名错误编码的问题
|
|
filename: Buffer.from(req.file.originalname, 'latin1').toString('utf8'),
|
|
password: req.body.password ? await hash(req.body.password, 16) : '',
|
|
})
|
|
console.log(`上传文件 ${file.filename}`)
|
|
res.status(201).render('upload', { title: '文件上传', file })
|
|
})
|
|
app.get('/download/:id', async (req, res) => {
|
|
try {
|
|
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', { title: '文件下载', file })
|
|
} catch (error) {
|
|
console.error(error)
|
|
res.sendStatus(404)
|
|
}
|
|
})
|
|
app.post('/download/:id', async (req, res) => {
|
|
try {
|
|
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 compare(req.body.password, file.password)) == true) {
|
|
await Filesharing.findByIdAndUpdate(req.params.id, { $inc: { downloads: 1 } })
|
|
console.log(`下载文件 ${file.filename}`)
|
|
res.status(200).download(path.join(UPLOAD_SAVE_PATH, file.md5), file.filename)
|
|
} else {
|
|
res.sendStatus(401)
|
|
}
|
|
} catch (error) {
|
|
console.error(error)
|
|
res.sendStatus(404)
|
|
}
|
|
})
|
|
app.get('/file/:md5', async (req, res) => {
|
|
try {
|
|
const file = await Filesharing.findOne({ md5: req.params.md5 })
|
|
if (!file) throw '试图下载不存在的文件'
|
|
if (file.password != '') throw '访问的文件需要密码'
|
|
res.sendFile(path.join(__dirname, UPLOAD_SAVE_PATH, req.params.md5))
|
|
} catch (error) {
|
|
console.error(error)
|
|
res.sendStatus(404)
|
|
}
|
|
})
|
|
|
|
app.listen(PORT, HOST, () => {
|
|
console.info(`server is running at http://${HOST}:${PORT}`)
|
|
})
|