const { SERVER_HOST, SERVER_PORT, MONGODB_URL, UPLOAD_TEMP_PATH, UPLOAD_SAVE_PATH, UPLOAD_SIZE_LIMIT } = require('./config') const fs = require('fs') const path = require('path') const multer = require('multer') const bcrypt = require('bcrypt') const md5file = require('md5-file') const filesize = require('filesize') const express = require('express') const mongoose = require('mongoose') const CronJob = require('cron').CronJob const Filesharing = require('./models/Filesharing') mongoose.connect(MONGODB_URL, (error) => { if (error) { console.error(error) } else { console.info('mongodb connected successfully') } }) 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 app = express() app.set('view engine', 'pug') app.use(express.static('public')) app.use(express.urlencoded({ extended: true })) app.use(express.json()) app.locals.filesize = filesize.partial({ base: 2, standard: 'jedec' }) 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') }) 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 bcrypt.hash(req.body.password, 16) : '', }) console.log(`上传文件 ${file.filename}`) res.status(201).render('upload', { 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', { 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 bcrypt.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) } }) const server = app.listen(SERVER_PORT, SERVER_HOST, () => { console.info(`server is running at http://${SERVER_HOST}:${SERVER_PORT}`) })