Koa搭建靜態資源伺服器
阿新 • • 發佈:2019-01-10
前言
之前使用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目錄下的所有靜態檔案了