一、需求

为 koa2 实现静态资源加载的服务,用于访问 css/js/img 等文件,访问图片的话,返回二进制文件,其他文件则直接输出字符串。

我下面自己的实现,过于简单或者没有考虑更多层面的东西,推荐 https://chenshenhai.github.io/koa2-note/note/static/server.html这篇文章。

二、实现路由和工具方法

项目目录:

微信截图_20180530213136.png

1、路由说明:

通过 /static/* 进行通用的 /static/img/1.png 类似路由匹配工作。

staticMiddleware 是核心的实现中间件(见第三点,实现中间件)。

const Router = require('koa-router');
const staticMiddleware = require('../middlewares/staticMiddleware');
const router = new Router();

router.use(staticMiddleware());

router.get('/',async (ctx,next)=>{
    ctx.body = `<h1>Index</h1>`;
});

router.get('static/*', async (ctx,next)=>{});

module.exports = router;

2、工具方法:extname 获取扩展名

位置:utils/extname.js

通过传入 ctx.url 进行分割,得到最后的扩展名

function extname(url) {
  let ext = url.split('.');
  ext = ext[ext.length - 1];
  return ext.toLowerCase();
}
module.exports = extname;

3、工具方法:getFilePath 获取文件的本地路径

位置: utils/getFilePath.js

没有做配置,直接写死的,其实本地是没有 static 这个目录,而是放在 ./public 目录中。

因此需要将 /static/ 去掉,拼上 public/

function getFilePath(url){
  const staticStr = '/static/';
  const public = 'public/';
  const filePath = url.substr(staticStr.length);
  return `${public}${filePath}`;
}

module.exports = getFilePath;

4、工具方法:getFileContent 读取文件内容

位置: utils/getFileContent.js

使用 fs.readFile() 进行异步文件读取,读取成功则 resolve ,失败则 reject

使用的 url 是通过 getFilePath() 返回的。

const fs = require('fs');
const path = require('path');

function getFile(url){
  return new Promise((resolve,reject)=>{
    fs.readFile(path.resolve(url),(err,data)=>{
      if(err) reject(err);
      resolve(data);
    });
  })
}

module.exports = getFile;

5、工具方法:mimetypes 返回文件类型

位置: utils/mimetypes.js

通过 extname 后缀获取文件的 mimetype,这个就直接照着 这篇文章 的内容写的一样。

const  mimetypes = {
  'css': 'text/css',
  'less': 'text/css',
  'gif': 'image/gif',
  'html': 'text/html',
  'ico': 'image/x-icon',
  'jpeg': 'image/jpeg',
  'jpg': 'image/jpeg',
  'js': 'text/javascript',
  'json': 'application/json',
  'pdf': 'application/pdf',
  'png': 'image/png',
  'svg': 'image/svg+xml',
  'swf': 'application/x-shockwave-flash',
  'tiff': 'image/tiff',
  'txt': 'text/plain',
  'wav': 'audio/x-wav',
  'wma': 'audio/x-ms-wma',
  'wmv': 'video/x-ms-wmv',
  'xml': 'text/xml'
}

module.exports = mimetypes;

三、实现中间件

引入需要的工具方法,其中 getStaticasync 方法,主要是因为 getFileContent() 通过异步文件读取,所以需要进行 await

getStatic() 方法主要是返回文件的内容、类型。

中间件在处理的时候,如果能够获取到文件,并且请求的文件是图片类型,则直接通过原生的 node.jsctx.res.write(obj.file, 'binary'); 进行二进制输出。其他的则输出字符串内容(readFile 读取的是 <Buffer>)。

文件位置:./middlewares/staticMiddleware

const extname = require('../utils/extame');
const mimetypes = require('../utils/mimetypes');
const getFileContent = require('../utils/getFileContent');
const getFilePath = require('../utils/getFilePath');

async function getStatic(ctx) {
  try{
    const ext = extname(ctx.url);
    const mimetype = mimetypes[ext];
    const filePath = getFilePath(ctx.url);
    let file = await getFileContent(filePath);
    return {code:0,file,mimetype};
  }catch(err){
    return {code:-1,err}
  }
}


module.exports = () => {
  return async (ctx, next) => {
    let obj = await getStatic(ctx);
    if(obj.code === -1) return ctx.res.writeHead(404);
    if(obj.mimetype) ctx.type = obj.mimetype;
    if(obj.mimetype && obj.mimetype.startsWith('image/')) {
      ctx.res.writeHead(200);
      ctx.res.write(obj.file, 'binary');
      ctx.res.end();
    }else{
        ctx.body = obj.file.toString();
    }
    await next();
  }
}

四、使用 koa-static 中间件

1、安装

yarn add koa-static

2、引入并使用

const Koa = require('koa');
const koaStatic = require('koa-static');

const app = new Koa();

// 相对于 app.js 的路径
const staticPath = './public';
// 使用中间件
app.use(koaStatic(path.join( __dirname,  staticPath)));

微信截图_20180530213136.png