基于 egg-ant-design-pro 实现上传图片裁剪的功能。 问题是这样的,egg-ant-design-pro 的 proxy 比较简单的做一个get 的代理,其他一些复杂一点的需求基本实现不了。所以进行了一些小修改。
但是在上传文件的时候遇到了问题。文件流传到上传服务器的时候就报错了。而且目测是整个进程也死了,需要重新npm run dev
才可以。
目前实现思路如下:
前端 通过dva的 request 把上传的文件传到 proxy ,通过了 const stream = await ctx.getFileStream()
获取到流,然后再通过formstream
内的 stream('file', stream, filename, mimeType, size)
方法把流挂上去。然后proxyObj.stream = form
传回给 const res = await this.ctx.curl(url, proxyObj);
。最后到上传的服务器项目,再通过 const stream = await ctx.getFileStream()
获取流。
具体代码如下。
egg-ant-design-pro 的 proxy 是这样的。
'use strict';
const Controller = require('egg').Controller;
class HomeController extends Controller {
async index() {
await this.ctx.render('index.html');
}
async proxy() {
const ctx = this.ctx;
// use roadhog mock api first
const url = 'http://127.0.0.1:8000' + ctx.path + '?' + ctx.querystring;
const res = await this.ctx.curl(url, {
method: this.ctx.method,
});
ctx.body = res.data;
ctx.status = res.status;
}
}
module.exports = HomeController;
目前改成
'use strict';
const Controller = require('egg').Controller;
const FormStream = require('formstream');
class HomeController extends Controller {
async index() {
await this.ctx.render('index.html');
}
async proxy() {
const { ctx, config, logger } = this;
const { body, header } = ctx.request;
// use roadhog mock api first
const url = `${config.apiPath}${ctx.path}?${ctx.querystring}`;
logger.info(`${ctx.method} ${url}`);
const proxyObj = {
method: ctx.method,
};
if (body) {
proxyObj.data = { ...body };
}
if (header) {
proxyObj.headers = { ...header };
if (header['content-type'] && header['content-type'].indexOf('multipart') >= 0) {
const stream = await ctx.getFileStream();
const form = new FormStream();
const { filename, mimeType, size } = stream;
form.stream('file', stream, filename, mimeType, size);
proxyObj.stream = form;
proxyObj.headers = {
...header,
...form.headers(header),
};
}
}
const res = await this.ctx.curl(url, proxyObj);
if (res.status !== 204) {
ctx.body = res.data;
}
ctx.status = res.status;
logger.info(`${ctx.method} ${url} ${res.status}`);
}
}
module.exports = HomeController;
文件上传服务器
const Controller = require('egg').Controller;
class UploadController extends Controller {
async img() {
const { ctx, service } = this;
console.log('img stream before');
const stream = await ctx.getFileStream();
const { qn } = service;
const { filename } = stream;
const res = await qn.upload(stream, filename);
ctx.helper.success({ ctx, res });
}
}
到 const res = await qn.upload(stream, filename);
这里就不行了。
[2018-12-12 19:15:17.416] [cfork:master:11561] worker:11563 disconnect (exitedAfterDisconnect: true, state: disconnected, isDead: false, worker.disableRefork: false)
[2018-12-12 19:15:17.416] [cfork:master:11561] don't fork new work (refork: false)
2018-12-12 19:15:17,416 INFO 11561 [master] app_worker#1:11563 disconnect, suicide: true, state: disconnected, current workers: ["1"]
[Wed Dec 12 2018 19:15:47 GMT+0800 (CST)] [graceful:worker:11563] kill timeout, exit now.
2018-12-12 19:15:47,405 ERROR 11563 [app_worker] exit with code:1
[2018-12-12 19:15:47.411] [cfork:master:11561] worker:11563 exit (code: 1, exitedAfterDisconnect: true, state: dead, isDead: true, isExpected: true, worker.disableRefork: false)
2018-12-12 19:15:47,412 ERROR 11561 nodejs.AppWorkerDiedError: [master] app_worker#1:11563 died (code: 1, signal: null, suicide: true, state: dead), current workers: []
at Master.onAppExit (/Users/thomas/Documents/projects/u_login_api/node_modules/egg-cluster/lib/master.js:398:21)
at Master.emit (events.js:180:13)
at Messenger.sendToMaster (/Users/thomas/Documents/projects/u_login_api/node_modules/egg-cluster/lib/utils/messenger.js:133:17)
at Messenger.send (/Users/thomas/Documents/projects/u_login_api/node_modules/egg-cluster/lib/utils/messenger.js:98:12)
at EventEmitter.cluster.on (/Users/thomas/Documents/projects/u_login_api/node_modules/egg-cluster/lib/master.js:267:22)
at EventEmitter.emit (events.js:185:15)
at ChildProcess.worker.process.once (internal/cluster/master.js:194:13)
at Object.onceWrapper (events.js:272:13)
at ChildProcess.emit (events.js:180:13)
at Process.ChildProcess._handle.onexit (internal/child_process.js:209:12)
name: "AppWorkerDiedError"
pid: 11561
const res = await qn.upload(stream, filename);
这里应该不是问题所在,因为在umi dev 的时候,直接到文件上传服务器是没有问题,可以正常上传到七牛云。
请问问题是在哪里,这里 proxy 对文件流的处理是否正确? 以上方法也是参考 egg官方文档 以-multipart-方式上传文件 来做的。
umi dev 代理时直接到 文件上传服务器 时的 const stream = await ctx.getFileStream()
{
"_readableState": {
"objectMode": false,
"highWaterMark": 16384,
"buffer": {
"head": {
"data": {
"type": "Buffer",
"data": [, 217]
},
"next": null
},
"tail": {
"data": {
"type": "Buffer",
"data": [255, 217]
},
"next": null
},
"length": 1
},
"length": 7178,
"pipes": null,
"pipesCount": 0,
"flowing": null,
"ended": true,
"endEmitted": false,
"reading": false,
"sync": false,
"needReadable": false,
"emittedReadable": true,
"readableListening": false,
"resumeScheduled": false,
"destroyed": false,
"defaultEncoding": "utf8",
"awaitDrain": 0,
"readingMore": false,
"decoder": null,
"encoding": null
},
"readable": true,
"_events": {},
"_eventsCount": 2,
"truncated": false,
"fieldname": "file",
"filename": "640.jpeg",
"encoding": "7bit",
"transferEncoding": "7bit",
"mime": "image/jpeg",
"mimeType": "image/jpeg",
"fields": {}
}
egg 的 proxy 代理后 文件上传服务器 时的 const stream = await ctx.getFileStream()
{
"_readableState": {
"objectMode": false,
"highWaterMark": 16384,
"buffer": {
"head": {
"data": {
"type": "Buffer",
"data": [255, 217]
},
"next": null
},
"tail": {
"data": {
"type": "Buffer",
"data": [255,217]
},
"next": null
},
"length": 1
},
"length": 8356,
"pipes": null,
"pipesCount": 0,
"flowing": null,
"ended": false,
"endEmitted": false,
"reading": true,
"sync": false,
"needReadable": true,
"emittedReadable": false,
"readableListening": false,
"resumeScheduled": false,
"destroyed": false,
"defaultEncoding": "utf8",
"awaitDrain": 0,
"readingMore": false,
"decoder": null,
"encoding": null
},
"readable": true,
"_events": {},
"_eventsCount": 2,
"truncated": false,
"fieldname": "file",
"filename": "640.jpeg",
"encoding": "7bit",
"transferEncoding": "7bit",
"mime": "image/jpeg",
"mimeType": "image/jpeg",
"fields": {}
}