遇到这个需求时候,疯狂百度搜索浏览几篇博客之后,看到解决方案大致分为两种:
- 代理上传流
- 先接收文件并写入,再代理请求上传
大多数文章都介绍的是第二种先接收文件并写入,再代理请求上传,这种方式无论是Egg自带的 HttpClient,还是superagent、request(已停止更新,不推荐使用)都是支持的。第一种代理上传流(单文件),目前只发现superagent支持(其他或许支持流,但需要先接收文件并写入)。接下来介绍解决方案。
先接收文件并写入,再代理请求上传
这种有很多种,本文只举例一个
依赖安装
npm i stream-wormhole await-stream-ready md5 -S
Service
'use strict';
const Service = require('egg').Service;
const path = require('path');
const sendToWormhole = require('stream-wormhole');
const fs = require('fs');
const awaitWriteStream = require('await-stream-ready').write;
const md5 = require('md5');
class formService extends Service {
async upload(url) {
const { ctx, config } = this;
const allUrl = config.api.headUrl + url;
const stream = await ctx.getFileStream();
// 为了不重名,一波操作猛如虎(不用文件内容计算md5,是怕文件内容过大)
const reName = md5(ctx.get('content-length') + new Date().getTime() + stream.filename) + stream.filename;
const target = path.join(config.baseDir, 'app/public', reName);
const writeStream = fs.createWriteStream(target);
let result;
try {
await awaitWriteStream(stream.pipe(writeStream));
result = await ctx.curl(allUrl, {
method: 'POST',
dataType: 'json',
headers: {
token: ctx.get('token'),
},
// 单文件上传
files: target,
});
} catch (err) {
// 如果出现错误,关闭管道
await sendToWormhole(stream);
ctx.throw(500, err);
}
// 在上传完毕后删除文件
fs.unlink(target, err => {
if (!err) {
console.log('文件已删除:', target);
}
});
return result;
}
}
module.exports = formService;
我不喜欢这种方式,因为需要写入文件,然后又删除。
代理上传流
依赖安装
npm i stream-wormhole superagent stream-to-array -S
Service
'use strict';
const Service = require('egg').Service;
const request = require('superagent');
const sendToWormhole = require('stream-wormhole');
const toArray = require('stream-to-array');
class formService extends Service {
async upload(url) {
const { ctx, config } = this;
const allUrl = config.api.headUrl + url;
const stream = await ctx.getFileStream();
let result;
try {
const arrStream = await toArray(stream);
const bufFile = Buffer.concat(arrStream);
result = await request
.post(allUrl)
.attach('file', bufFile, stream.filename)
.set('token', ctx.get('token'));
} catch (err) {
// 如果出现错误,关闭管道
await sendToWormhole(stream);
ctx.throw(500, err);
}
return {
...result,
data: result.body,
};
}
}
module.exports = formService;
参考链接:
https://juejin.im/post/6844903969865777165
https://www.yuque.com/egg/nodejs/httpclient-upload
https://www.jianshu.com/p/56bfdae6f5c6