0.前面
技术栈:eggjs + axios + request + vue
真的是摸着石头过河,对技术栈的源码、API不熟悉,不知道能功能可以实现到什么程度。只能一步一步的“写代码 -> 运行代码 -> 测试功能是否实现”,就像胡乱建一栋房子一样,可以保证建的时候不塌,至于以后塌不塌、漏不漏风,就不清楚。只能在使用过程中,不断维护了。
1.第一个版本(eggjs + axios)
async upload() {
const { logger, service, ctx } = this;
const stream = await ctx.getFileStream();
try {
const { query } = URL.parse(ctx.url, true);
const name = query.name || stream.fields.name || stream.filename;
const md5 = query.md5 || stream.fields.md5;
const pid = query.pid || stream.fields.pid;
const formData = new FormData();
formData.append('name', name);
formData.append('md5', md5);
formData.append('pid', pid);
formData.append('file', stream, {
filename: name,
header: `--${formData.getBoundary()}\r\nContent-Disposition: form-data; name="file"; filename=${name}\r\nContent-Type: octet-stream\r\n\r\n`
});
const options = {
method: 'post',
data: formData,
headers: {
'content-type': 'multipart/form-data; boundary=' + formData.getBoundary()
}
};
return await this.request(options);
} catch (e) {
logger.debug(e);
ctx.response.status = 400;
ctx.body = {
code: 400,
message: '上传失败:未知错误!',
};
} finally {
// must consume the file stream, or the browser will get stuck
if (stream) { await sendToWormhole(stream); }
}
}
这里出错了很久,eggjs的ctx.getFileStream()经过了处理,FormData添加file字段的时候总是报错。研究源码,不断测试,发现自动处理header出错,后来手动在file字段加上header才成功上传。
formData.append('file', stream, {
filename: name,
header: `--${formData.getBoundary()}\r\nContent-Disposition: form-data; name="file"; filename=${name}\r\nContent-Type: octet-stream\r\n\r\n`
});
后来,测试发现不支持上传大文件(一百多M),上传十几M就失败停止,应该是分块上传的时候,没有处理后面的数据。实在不知道axios是怎么处理这种情况的,没办法,只能使用其他技术。
2.第二个版本(eggjs + request)
async upload() {
const { logger, config, ctx } = this;
let url = ctx.url.replace(/^\/api/, '');
url = (config.api.host || 'http://127.0.0.1:8080') + url;
logger.info(`post ${url}`);
const func = (resolve, reject) => {
const x = request(url, (error, response, data) => {
if (!error && response.statusCode === 200) {
response.data = data;
resolve(response);
} else {
reject({
code: 'error',
result: 'fail',
message: 'API服务器异常'
});
}
});
ctx.req.pipe(x); // 核心语句
};
ctx.body = await new Promise(func)
.then(response => {
return response.data;
})
.catch((error) => {
logger.error('API服务器异常', error && error.code, error && error.message);
return error;
});
}
一开始使用http-proxy,但不支持eggjs,以下代码是返回不了数据的,不过现在想想,应该给ctx.res加个data事件处理就行了。
proxy(ctx.req, ctx.res, { target });
然后又找到egg-proxy,但这是个插件,不符合我的需求,所以我就研究他的源码,看他怎么解决文件上传代理的。一路找到request(egg-proxy -> koa-proxy -> co-request -> request),对于co-request实在看不明白,在eggjs下,以下代码的res是个方法,获取不了数据的,应该是需要增加一些异步处理关键字。
改为eggjs环境源码(res是方法,获取不了数据) | koa-proxy相关源码 |
| |
最后只能使用request,并加上promise异步处理。
3.第三个版本(eggjs + axios)
还是想用回axios,毕竟突然出现一个repuest,强迫症受不了。
目前已经有了一点思路,Ability to pipe read stream to PUT/POST request (Node) #553。