1. 程式人生 > >Koa搭建靜態資源伺服器

Koa搭建靜態資源伺服器

前言

之前使用egg寫了個簡版的軟文後臺,順手就是使用了jq去寫介面,後來隨著功能越來越多,突然就覺得以前寫的jq越來越難維護,頻繁需要去操作dom,修改各種頁面的狀態值。於是打算使用vue重構一版,將其元件化,順手也就把egg換成了,感覺這種簡單的後端需求,koa確實已經足夠了。
因為軟文後臺涉及到對圖片的處理,我們希望後端能提供一個地址去瀏覽這些已經處理的圖片,所以就有了搭建一個靜態資源伺服器的需求。
博文中用到的程式碼並非原創,我只是做了一些優化。

Koa-static

Koa中介軟體-koa-static能將專案的某個目錄(一般是static或者public目錄)的檔案對映到路由上,使得這些檔案能通過url

在瀏覽器訪問到。
koa-static並沒有列出所有檔案的功能,想要實現這一功能,只能自己寫中介軟體處理了

中介軟體

最後的效果圖

static.js

utils目錄下新建靜態資源處理的js—static.js

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

let mimes = {
  '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' } /** * 遍歷讀取目錄內容(子目錄,檔名) * @param {string} reqPath 請求資源的絕對路徑 * @return {array} 目錄內容列表 */ function walk (reqPath) { let files = fs.readdirSync(reqPath) let dirList = [] let fileList = [] for (let i = 0, len = files.length; i < len; i++) { let item = files[i] let itemArr = item.split('.') let itemMime = (itemArr.length > 1) ? itemArr[ itemArr.length - 1 ] : 'undefined' if (typeof mimes[ itemMime ] === 'undefined') { dirList.push(files[i]) } else { fileList.push(files[i]) } } let result = dirList.concat(fileList) return result } /** * 封裝目錄內容 * @param {string} url 當前請求的上下文中的url,即ctx.url * @param {string} reqPath 請求靜態資源的完整本地路徑 * @return {string} 返回目錄內容,封裝成HTML */ function dir (url, reqPath) { // 遍歷讀取當前目錄下的檔案、子目錄 let contentList = walk(reqPath) let html = `<ul>` for (let [ index, item ] of contentList.entries()) { html = `${html}<li data-index="${index}"><a href="${url === '/' ? '' : url}/${item}">${item}</a>` } html = `${html}</ul>` return html } /** * 讀取檔案方法 * @param {string} 檔案本地的絕對路徑 * @return {string|binary} */ function file (filePath) { let content = fs.readFileSync(filePath, 'binary') return content } /** * 獲取靜態資源內容 * @param {object} ctx koa上下文 * @param {string} 靜態資源目錄在本地的絕對路徑 * @return {string} 請求獲取到的本地內容 */ async function content (ctx, fullStaticPath) { // 封裝請求資源的完絕對徑 let reqPath = path.join(fullStaticPath, ctx.url) // 判斷請求路徑是否為存在目錄或者檔案 let exist = fs.existsSync(reqPath) // 返回請求內容, 預設為空 let content = '' if (!exist) { // 如果請求路徑不存在,返回404 content = '請求路徑不存在!' } else { // 判斷訪問地址是資料夾還是檔案 let stat = fs.statSync(reqPath) if (stat.isDirectory()) { // 如果為目錄,則渲讀取目錄內容 content = dir(ctx.url, reqPath) } else { // 如果請求為檔案,則讀取檔案內容 content = await file(reqPath) } } return content } // 解析資源型別 function parseMime (url) { let extName = path.extname(url) extName = extName ? extName.slice(1) : 'unknown' return mimes[ extName ] } module.exports = { parseMime: parseMime, content: content }

ps: 我把大神的程式碼合併了下,感覺新建五個js還是有點多。另外注意宣告中介軟體的順序,api的宣告應當放在static中介軟體的前面,否則api宣告的路由就都失效了

router中新增路由

// 靜態資源伺服器
router.get('/static', async (ctx) => {
  // 靜態資源目錄在本地的絕對路徑
  let fullStaticPath = path.join(__dirname, '../')

  // 獲取靜態資源內容,有可能是檔案內容,目錄,或404
  let _content = await staticUtil.content(ctx, fullStaticPath)

  // 解析請求內容的型別
  let _mime = staticUtil.parseMime(ctx.url)

  // 如果有對應的檔案型別,就配置上下文的型別
  if (_mime) {
    ctx.type = _mime
  }

  // 輸出靜態資源內容
  if (_mime && _mime.indexOf('image/') >= 0) {
    // 如果是圖片,則用node原生res,輸出二進位制資料
    ctx.res.writeHead(200)
    ctx.res.write(_content, 'binary')
    ctx.res.end()
  } else {
    // 其他則輸出文字
    ctx.body = _content
  }
})

這樣當你訪問http://localhost:3000/static就能列出位於static目錄下的所有靜態檔案了

結束