index.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. //原生模块
  2. let http = require('http')
  3. let fs = require('fs')
  4. let path = require('path')
  5. let url = require('url')
  6. let { promisify, inspect } = require('util')
  7. let stat = promisify(fs.stat)
  8. let readdir = promisify(fs.readdir)
  9. let zlib = require('zlib')
  10. //第三方包
  11. let chalk = require('chalk')
  12. let mime = require('mime')
  13. let multiparty = require('multiparty')
  14. //自定义模块
  15. let config = require('./config.js')
  16. let compileHtml = require('./generater/compileHtml.js')
  17. let handleFile = require('./generater/handleFile.js')
  18. class Server {
  19. constructor(argv) {
  20. //挂载模版方法
  21. this.compileHtml = compileHtml()
  22. //挂在配置,处理默认值
  23. this.config = Object.assign({}, config, argv)
  24. }
  25. //启动http服务器
  26. start() {
  27. let server = http.createServer()
  28. server.on('request', this.request.bind(this))
  29. server.listen(this.config.port, () => {
  30. let url = `http://${this.config.host}:${this.config.port}`;
  31. console.log(`${chalk.blue('server has started at')} ${chalk.green(url)}`);
  32. })
  33. }
  34. //http服务器请求处理函数
  35. async request(req, res) {
  36. //处理跨域
  37. let domain = 'http://' + req.headers['host']
  38. this.handleOption(req, res, domain)
  39. if (req.method == 'OPTIONS') {
  40. return res.end()
  41. }
  42. let { pathname } = url.parse(req.url)
  43. //读文件目录
  44. let filepath = path.join(this.config.document, pathname)
  45. //处理分块传输
  46. if (pathname == '/upfile' && req.method == 'POST') {
  47. return this.handleChunk(req, res, this.config.document)
  48. }
  49. //如果是文件夹,读取文件夹发送目录,不是文件夹返回文件内容
  50. try {
  51. let fileStat = await stat(filepath)
  52. if (fileStat.isDirectory()) {
  53. let files = await readdir(filepath)
  54. files = files.map(file => {
  55. return {
  56. name: file,
  57. url: path.join(pathname, file)
  58. }
  59. })
  60. this.sendDirectory(req, res, filepath, files)
  61. } else {
  62. let ifCatch = this.handleCatch(req, res, filepath, fileStat)
  63. //如果走缓存,则直接返回
  64. if (ifCatch) return
  65. this.sendFile(req, res, filepath)
  66. }
  67. } catch (err) {
  68. this.sendError(err, res)
  69. }
  70. }
  71. //处理断点续传
  72. handleChunk(req, res, filepath) {
  73. let form = new multiparty.Form();
  74. form.parse(req, function (err, fields, files) {
  75. if (err) {
  76. console.error('multiparty错误:', err)
  77. }
  78. if (files) {
  79. handleFile(req, res, fields, files, filepath)
  80. }
  81. });
  82. }
  83. //处理跨域等
  84. handleOption(req, res, domain) {
  85. //cors跨域
  86. res.setHeader('Access-Control-Allow-Origin', domain)
  87. res.setHeader('Access-Control-Allow-Credentials', true)
  88. res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS")
  89. res.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With,Range")
  90. }
  91. //缓存处理函数
  92. handleCatch(req, res, filepath, fileStat) {
  93. //强制缓存
  94. res.setHeader('Expries', new Date(Date.now() + 30 * 1000).toGMTString())
  95. res.setHeader('Catch-Control', 'private,max-age=30')
  96. //对比缓存
  97. let ifModifiedSince = req.headers['if-modified-since']
  98. let ifNoneMatch = req.headers['if-none-match']
  99. let lastModified = fileStat.ctime.toGMTString()
  100. let eTag = fileStat.size
  101. res.setHeader('Last-Modified', lastModified)
  102. res.setHeader('ETag', eTag)
  103. //任何一个对比缓存头不匹配,则不走缓存
  104. if (ifModifiedSince && ifModifiedSince != lastModified) {
  105. return false
  106. }
  107. if (ifNoneMatch && ifNoneMatch != eTag) {
  108. return false
  109. }
  110. //当请求中存在任何一个对比缓存头,则返回304,否则不走缓存
  111. if (ifModifiedSince || ifNoneMatch) {
  112. res.writeHead(304)
  113. res.end()
  114. return true
  115. } else {
  116. return false
  117. }
  118. }
  119. //发送文件夹目录
  120. sendDirectory(req, res, filepath, files) {
  121. res.setHeader('Content-Type', 'text/html')
  122. //用模版引擎编译模版,得到编译后的结果
  123. let html = this.compileHtml({ title: 'bin2', files })
  124. res.end(html)
  125. }
  126. //发送文件
  127. sendFile(req, res, filepath) {
  128. res.setHeader('Content-Type', mime.getType(filepath) + ';charset=utf-8')
  129. let readStream = fs.createReadStream(filepath)
  130. //处理压缩
  131. let zlibStream = this.handleZlib(req, res)
  132. if (zlibStream) {
  133. readStream.pipe(zlibStream).pipe(res)
  134. } else {
  135. readStream.pipe(res)
  136. }
  137. }
  138. //处理压缩
  139. handleZlib(req, res) {
  140. let acceptEncoding = req.headers['accept-encoding']
  141. if (/\bgzip\b/g.test(acceptEncoding)) {
  142. res.setHeader('Content-Encoding', 'gzip');
  143. return zlib.createGzip()
  144. } else if (/\bdeflate\b/g.test(acceptEncoding)) {
  145. res.setHeader('Content-Encoding', 'deflate');
  146. return zlib.createDeflate()
  147. } else {
  148. return null
  149. }
  150. }
  151. //错误处理函数
  152. sendError(err, res) {
  153. res.statusCode = 500;
  154. res.end(`${err.toString()}`);
  155. }
  156. }
  157. module.exports = Server