HoRain云小助手:个人主页
个人专栏: 《Linux 系列教程》《c语言教程》
⛺️生活的理想,就是为了理想的生活!
前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。
专栏名称 |
专栏介绍 |
《C语言》 |
本专栏主要撰写C干货内容和编程技巧,让大家从底层了解C,把更多的知识由抽象到简单通俗易懂。 |
《网络协议》 |
本专栏主要是注重从底层来给大家一步步剖析网络协议的奥秘,一起解密网络协议在运行中协议的基本运行机制! |
《docker容器精解篇》 |
全面深入解析 docker 容器,从基础到进阶,涵盖原理、操作、实践案例,助您精通 docker。 |
《linux系列》 |
本专栏主要撰写Linux干货内容,从基础到进阶,知识由抽象到简单通俗易懂,帮你从新手小白到扫地僧。 |
《python 系列》 |
本专栏着重撰写Python相关的干货内容与编程技巧,助力大家从底层去认识Python,将更多复杂的知识由抽象转化为简单易懂的内容。 |
《试题库》 |
本专栏主要是发布一些考试和练习题库(涵盖软考、HCIE、HRCE、CCNA等) |
目录
⛳️ 推荐
专栏介绍
一、环境准备
1. 项目初始化
2. 目录结构
二、基础下载服务实现
1. 快速启动服务(app.js)
2. 测试文件下载
三、进阶功能实现
1. 大文件分片下载(支持断点续传)
2. 下载权限控制(middlewares/auth.js)
四、安全防护配置
1. 安全标头设置(config/security.js)
2. 下载频率限制
五、生产环境优化
1. Nginx反向代理配置
2. 集群模式启动
六、监控与日志
1. 访问日志配置
2. 下载统计接口
七、部署与维护
1. PM2进程管理
2. 备份脚本
八、常见问题排查
1. 文件下载不完整
2. 内存泄漏检测
3. 性能优化建议
mkdir file-server && cd file-server
npm init -y
npm install express compression cors helmet morgan
├── config/ # 配置文件
│ └── security.js
├── middlewares/ # 自定义中间件
│ └── auth.js
├── routes/ # 路由文件
│ └── download.js
├── storage/ # 文件存储目录
│ ├── public/ # 公开文件
│ └── private/ # 授权文件
└── app.js # 主入口文件
const express = require('express')
const app = express()
const port = 3000
// 静态文件服务
app.use('/static', express.static('storage/public'))
// 下载路由
app.get('/download/:filename', (req, res) => {
const filePath = `${__dirname}/storage/public/${req.params.filename}`
res.download(filePath, err => {
if (err) res.status(404).send('File not found')
})
})
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`)
})
# 创建测试文件
echo "Hello World" > storage/public/test.txt
# 使用curl测试下载
curl -O http://localhost:3000/download/test.txt
const fs = require('fs')
const path = require('path')
app.get('/download-chunk/:filename', (req, res) => {
const filePath = path.join(__dirname, 'storage/public', req.params.filename)
const stat = fs.statSync(filePath)
const fileSize = stat.size
const range = req.headers.range
if (range) {
const parts = range.replace(/bytes=/, "").split("-")
const start = parseInt(parts[0], 10)
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1
const chunkSize = (end - start) + 1
res.writeHead(206, {
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunkSize,
'Content-Type': 'application/octet-stream'
})
const stream = fs.createReadStream(filePath, { start, end })
stream.pipe(res)
} else {
res.writeHead(200, {
'Content-Length': fileSize,
'Content-Type': 'application/octet-stream'
})
fs.createReadStream(filePath).pipe(res)
}
})
const apiKeys = new Set(process.env.API_KEYS?.split(',') || [])
module.exports = (req, res, next) => {
const apiKey = req.headers['x-api-key'] || req.query.api_key
if (!apiKeys.has(apiKey)) {
return res.status(403).json({
error: 'Invalid API Key',
code: 'AUTH_REQUIRED'
})
}
next()
}
const helmet = require('helmet')
module.exports = helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:"]
}
},
hsts: {
maxAge: 63072000, // 2年
includeSubDomains: true
}
})
const rateLimit = require('express-rate-limit')
const downloadLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 50, // 每个IP限制50次请求
standardHeaders: true,
message: 'Too many download requests, please try later'
})
app.use('/download', downloadLimiter)
server {
listen 80;
server_name files.example.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 文件下载优化参数
proxy_buffering off;
proxy_request_buffering off;
proxy_max_temp_file_size 0;
}
# 静态文件缓存
location /static {
expires 30d;
add_header Cache-Control "public";
}
}
const cluster = require('cluster')
const os = require('os')
if (cluster.isPrimary) {
const cpuCount = os.cpus().length
for (let i = 0; i < cpuCount; i++) {
cluster.fork()
}
} else {
app.listen(process.env.PORT || 3000)
}
const morgan = require('morgan')
const fs = require('fs')
const path = require('path')
// 创建日志目录
const accessLogStream = fs.createWriteStream(
path.join(__dirname, 'logs/access.log'),
{ flags: 'a' }
)
app.use(morgan('combined', {
stream: accessLogStream,
skip: (req) => req.path === '/healthcheck'
}))
const downloadStats = new Map()
app.get('/stats', (req, res) => {
const stats = Array.from(downloadStats.entries()).map(([file, count]) => ({
file,
downloads: count
}))
res.json({ data: stats })
})
// 在下载路由中增加统计
app.use('/download', (req, res, next) => {
const filename = req.params.filename
downloadStats.set(filename, (downloadStats.get(filename) || 0) + 1)
next()
})
npm install pm2 -g
pm2 start app.js -i max --name "file-server"
pm2 save
pm2 startup
#!/bin/bash
BACKUP_DIR="/backup/fileserver-$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR
cp -r storage $BACKUP_DIR
mysqldump -u root -p your_db > $BACKUP_DIR/db.sql
tar -czf $BACKUP_DIR.tar.gz $BACKUP_DIR
# 检查文件系统inode限制
df -i
# 增加文件描述符限制
ulimit -n 65536
const heapdump = require('heapdump')
process.on('SIGUSR2', () => {
const filename = `heapdump-${Date.now()}.heapsnapshot`
heapdump.writeSnapshot(filename)
console.log(`Heap snapshot written to ${filename}`)
})
// 使用流式处理大文件
app.get('/download-stream/:filename', (req, res) => {
const fileStream = fs.createReadStream(filePath)
fileStream.pipe(res)
fileStream.on('error', (err) => {
res.status(500).send('File stream error')
})
})
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!
如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!
Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!