美文网首页
koa静态文件处理

koa静态文件处理

作者: 马贞晓 | 来源:发表于2018-11-05 11:14 被阅读0次

    koa使用koa-staitc来处理静态文件

    其核心使用koa-send组件来完成,对齐文件进行解析,它对文件进行了几项处理
    1、对组件root目录,和请求路径进行合并,并对返回文件头进行处理,主要针对.br 和.gz

    app.use(static(你要制定的静态文件根目录)) 。
    所以你要请求的路径中不能带入跟目录相关字段,直接请求文件即可,请求早于其他使用router包请求处理,所以尽量不要和后面的非静态路径请求冲突。

    以下最主要就一句ctx.body = fs.createReadStream(path),加上文件头就是核心。

    /**
     * Module dependencies.
     */
    
    const debug = require('debug')('koa-send')
    const resolvePath = require('resolve-path')
    const createError = require('http-errors')
    const assert = require('assert')
    const fs = require('mz/fs')
    
    const {
      normalize,
      basename,
      extname,
      resolve,
      parse,
      sep
    } = require('path')
    
    /**
     * Expose `send()`.
     */
    
    module.exports = send
    
    /**
     * Send file at `path` with the
     * given `options` to the koa `ctx`.
     *
     * @param {Context} ctx
     * @param {String} path
     * @param {Object} [opts]
     * @return {Function}
     * @api public
     */
    
    async function send (ctx, path, opts = {}) {
      assert(ctx, 'koa context required')
      assert(path, 'pathname required')
    
      // options
      debug('send "%s" %j', path, opts)
      const root = opts.root ? normalize(resolve(opts.root)) : ''
      const trailingSlash = path[path.length - 1] === '/'
      path = path.substr(parse(path).root.length)
      const index = opts.index
      const maxage = opts.maxage || opts.maxAge || 0
      const immutable = opts.immutable || false
      const hidden = opts.hidden || false
      const format = opts.format !== false
      const extensions = Array.isArray(opts.extensions) ? opts.extensions : false
      const brotli = opts.brotli !== false
      const gzip = opts.gzip !== false
      const setHeaders = opts.setHeaders
    
      if (setHeaders && typeof setHeaders !== 'function') {
        throw new TypeError('option setHeaders must be function')
      }
    
      // normalize path
      path = decode(path)
    
      if (path === -1) return ctx.throw(400, 'failed to decode')
    
      // index file support
      if (index && trailingSlash) path += index
    
      path = resolvePath(root, path)
    
      // hidden file support, ignore
      if (!hidden && isHidden(root, path)) return
    
      let encodingExt = ''
      // serve brotli file when possible otherwise gzipped file when possible
      if (ctx.acceptsEncodings('br', 'identity') === 'br' && brotli && (await fs.exists(path + '.br'))) {
        path = path + '.br'
        ctx.set('Content-Encoding', 'br')
        ctx.res.removeHeader('Content-Length')
        encodingExt = '.br'
      } else if (ctx.acceptsEncodings('gzip', 'identity') === 'gzip' && gzip && (await fs.exists(path + '.gz'))) {
        path = path + '.gz'
        ctx.set('Content-Encoding', 'gzip')
        ctx.res.removeHeader('Content-Length')
        encodingExt = '.gz'
      }
    
      if (extensions && !/\.[^/]*$/.exec(path)) {
        const list = [].concat(extensions)
        for (let i = 0; i < list.length; i++) {
          let ext = list[i]
          if (typeof ext !== 'string') {
            throw new TypeError('option extensions must be array of strings or false')
          }
          if (!/^\./.exec(ext)) ext = '.' + ext
          if (await fs.exists(path + ext)) {
            path = path + ext
            break
          }
        }
      }
    
      // stat
      let stats
      try {
        stats = await fs.stat(path)
    
        // Format the path to serve static file servers
        // and not require a trailing slash for directories,
        // so that you can do both `/directory` and `/directory/`
        if (stats.isDirectory()) {
          if (format && index) {
            path += '/' + index
            stats = await fs.stat(path)
          } else {
            return
          }
        }
      } catch (err) {
        const notfound = ['ENOENT', 'ENAMETOOLONG', 'ENOTDIR']
        if (notfound.includes(err.code)) {
          throw createError(404, err)
        }
        err.status = 500
        throw err
      }
    
      if (setHeaders) setHeaders(ctx.res, path, stats)
    
      // stream
      ctx.set('Content-Length', stats.size)
      if (!ctx.response.get('Last-Modified')) ctx.set('Last-Modified', stats.mtime.toUTCString())
      if (!ctx.response.get('Cache-Control')) {
        const directives = ['max-age=' + (maxage / 1000 | 0)]
        if (immutable) {
          directives.push('immutable')
        }
        ctx.set('Cache-Control', directives.join(','))
      }
      if (!ctx.type) ctx.type = type(path, encodingExt)
      ctx.body = fs.createReadStream(path)
    
      return path
    }
    
    /**
     * Check if it's hidden.
     */
    
    function isHidden (root, path) {
      path = path.substr(root.length).split(sep)
      for (let i = 0; i < path.length; i++) {
        if (path[i][0] === '.') return true
      }
      return false
    }
    
    /**
     * File type.
     */
    
    function type (file, ext) {
      return ext !== '' ? extname(basename(file, ext)) : extname(file)
    }
    
    /**
     * Decode `path`.
     */
    
    function decode (path) {
      try {
        return decodeURIComponent(path)
      } catch (err) {
        return -1
      }
    }
    
    

    相关文章

      网友评论

          本文标题:koa静态文件处理

          本文链接:https://www.haomeiwen.com/subject/vynjxqtx.html