nodejs从ftp获取文件内容(非文件管道)

起初的源码:

router.post("/getfile", (req, res) => {
	let c = new Client();
	let params = req.query;
	let filePath = params.filepath;

 	c.on('ready', function () {
	    c.get(filePath, function (err, stream) {
			if (err) {
				res.end(JSON.stringify({
					code: 1,
					message: err,
	          		data: ''
	        	}));
	        	return '';
	      	}

	      	stream.once('close', function () {
	        	c.end();
	      	});
	      	stream.pipe(res);
	    });
	});
  	c.connect(connectKey)
});

问题说明:

打log后发现进入路由的时间很快,并且执行速度也很快,毫秒级别的运行时间,但是接口的返回显得很慢,所以是stream.pipe(res);的相关问题

问题分析

我打算分析pipe相关的源码,但是网上没找到资源,只找到nodejs的api中有对pipe和流的其他用法,所以最后的解决方法用的是流的读取。我把它放在

ftp在ready后获取err和stream两个参数,其中stream是流的含义,所以在官方文档中找到流的文件读写方法,并通过eventEmitter事件触发各类操作,列出如下:

  • close —— writeable
    流及其任何底层资源(例如文件描述符)已关闭时,则会触发 ‘close’ 事件
  • error —— writeable
    如果在写入或管道数据时发生错误,则会触发 ‘error’ 事件
  • finish —— writeable
    在调用stream.end()方法之后,并且所有数据都已刷新到底层系统,则触发 ‘finish’ 事件
  • pipe —— writeable
    当在可读流上调用 stream.pipe() 方法将此可写流添加到其目标集时,则触发 ‘pipe’ 事件
    ···可写流就先写这么多,暂时用不到

既然是管道的方法,我认为是服务器和本机没有缓存的原因,导致ftp的传输是字节流,所以响应速度很慢,但是在多次实践中发现读取时间一直是稳定在10s左右,所以排除字节流的读取方法。

另一种猜想也和合理的是管道运输需要一个目的缓存,但是直接使用pipe方法的res没有文件流的接收方,数据就会堵在发送方,直至管道结束。

解决方法(另一种读取方案)

我当时的解决方法是使用流的读取事件获取chunk,最后返回给res。源码如下

router.post("/getfile", (req, res) => {
	let c = new Client();
	let params = req.query;
	let filePath = params.filepath;

 	c.on('ready', function () {
	    c.get(filePath, function (err, stream) {
			if (err) {
				res.end(JSON.stringify({
					code: 1,
					message: err,
	          		data: ''
	        	}));
	        	return '';
	      	}
	      	let data = '',
	          	chunk = '';
	      	stream.on('close', function () {
	        	c.end();
	      	});
	      	stream.on('readable', function () {
	       		while(null != (chunk = stream.read())) {
	          		data += chunk;
	        	}
	        	setTimeout(() => {
	          		res.end(JSON.stringify({
	            		code: 0,
	            		message: '',
	            		data: data
	          		}));
	        	});
	      	});
	    });
	});
  	c.connect(connectKey)
});

根据官方文档描述:

每次调用 readable.read() 都会返回一个数据块或 null。 块不是串联的。 需要 while 循环来消费当前缓冲区中的所有数据。 当读取大文件时,.read() 可能会返回 null,到目前为止已经消费了所有缓冲的内容,但是还有更多的数据尚未缓冲。 在这种情况下,当缓冲区中有更多数据时,将触发新的 ‘readable’ 事件。 最后,当没有更多数据时,则将触发 ‘end’ 事件。

所以在没有可传输文件时,流的readable会触发一个end信号;但在实际应用中,end并没有触发,目前也不清楚原理,而且close不是立即触发的,也是10s后才触发。不过这种写法可以很快返回res。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值