当前许多前端应用都存在node层,并通过node层做上传/下载代理(当然通过nginx代理应该是首选),本文主要介绍几个代理的插件和其中存在的坑,希望对大家的上传/下载研发方案有帮助。
nodejs环境:10.18.0 | eggjs版本:2.29.4 | koa-proxies版本:0.12.1 | koa2-nginx:2.0.2
代理插件
本文不是介绍插件怎么用的,如需详细了解相关知识,可以直接看插件的文档或者代码,本文主要介绍功能实现,需要问题和解决办法。本文主要需求是通过post来实现参数传递,流文件实现下载。
(1)koa2-proxies
地址:https://github.com/vagusX/koa-proxies
存在问题:这个版本对于Content-Type: multipart/form-data能够很好的处理,但是对于Content-Type: application/json会导致请求头和body无法上传(这个已经有人提bug了,见图1),导致nginx端读取不到数据而出现连接超时408错误。
如下代码片段是一个典型的代理实现。
router.post(
'/downloadproxy',
proxy('/download', params => {
return {
target,
changeOrigin: true,
logs: true,
rewrite: path => {
return path.replace('/downloadproxy', '/download')
}
}
})
)
(2)koa2-nginx
npm地址:https://www.npmjs.com/package/koa2-nginx
这个插件是解决了koa2-proxies对于body的处理问题,需要加入参数autoProcessReqBody。
如下是典型的实现应用。
await proxy({
context: '/downloadproxy',
pathRewrite: {
'^/downloadproxy': ''
},
target,
changeOrigin: true,
autoProcessReqBody: true
})(this.ctx)
(3)非插件解决body缺失问题
当请求的 Content-Type 为 application/json,application/json-patch+json,application/vnd.api+json 和 application/csp-report 时,eggjs会按照json格式对请求body进行解析,并限制body最大长度为100kb。
回到上面的问题,为啥会丢失body,主要是eggjs的bodyparser默认为开启状态,这时候,它会消费从页面发送来的body,导致body缺失,会让nginx认为这个请求等待header信息,最后超时。这时候可以这样设置,关闭bodyparser:
exports.bodyParser = {
ignore: ['/api/download/*'], //需要忽略body消费的api匹配规则
};
下载请求
通过axios请求下载文件时需要注意配置responseType,一般通过Blob的方式来缓存stream流数据,并保存为其他格式,比如保存为zip文件,这是responseType必须设置为blob,否则zip文件将打开出错。
axios的典型实现如下:
axios({
method: 'post',
url: '/download',
data: {
parameters: 'some parameters'
},
responseType: 'blob'
})
其中responseType的主要类型如下:
// 选项包括: 'arraybuffer', 'document', 'json', 'text', 'stream'
// 浏览器专属:'blob'
responseType: 'json', // 默认值
保存文件的实现方式
const contentType = res['headers']['content-type'] || res['headers']['Content-Type']
const fileName = res['headers']['content-disposition'].split('filename=')[1]
if (!fileName) {
toBuffer(res.data, function(err, buffer) {
const str = buffer.toString() || ''
console.log('下载失败')
})
} else {
const url = window.URL.createObjectURL(new Blob([res.data], { type: contentType }))
const downloadElement = document.createElement('a')
downloadElement.href = url
downloadElement.setAttribute('download', fileName)
document.body.appendChild(downloadElement)
downloadElement.click()
document.body.removeChild(downloadElement)
window.URL.revokeObjectURL(url)
}
ps: 通过Node层进行代理上传下载的坑比较多,推荐使用Nginx能省去不少麻烦。