有关于 Transfer-Encoding:chunked 类型的响应,参见之前的文章HTTP 响应的分块传输。这里看 Koa 中如何实现。

Koa 中请求返回的处理

虽然官方文档有描述说明不建议直接调用 response.write

Bypassing Koa's response handling is not supported. Avoid using the following node properties:

  • res.statusCode
  • res.writeHead()
  • res.write()
  • res.end()

但要实现分片向客户端发送数据,必然还是得调用 Node.js Http 模块的 response.write(chunk[, encoding][, callback]) 方法,而这里的 response 就是 ctx.resctx.response

所以为什么 Koa 要说不建议直接调用上述方法操作请求的返回呢,我们来看看 Koa 内部对 response 都会做些什么默认的处理。

application.js

  handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}

在应用完各种中间件后(fnMiddleware(ctx))通过 handleResponse 对请求进行一些操作,最终是在 respond 函数里。

respond 方法
function respond(ctx) {
// allow bypassing koa
if (false === ctx.respond) return; if (!ctx.writable) return; const res = ctx.res;

let body = ctx.body;

const code = ctx.status; // ignore body

if (statuses.empty[code]) {

// strip headers

ctx.body = null;

return res.end();

} if ('HEAD' == ctx.method) {

if (!res.headersSent && isJSON(body)) {

ctx.length = Buffer.byteLength(JSON.stringify(body));

}

return res.end();

} // status body

if (null == body) {

if (ctx.req.httpVersionMajor >= 2) {

body = String(code);

} else {

body = ctx.message || String(code);

}

if (!res.headersSent) {

ctx.type = 'text';

ctx.length = Buffer.byteLength(body);

}

return res.end(body);

} // responses

if (Buffer.isBuffer(body)) return res.end(body);

if ('string' == typeof body) return res.end(body);

if (body instanceof Stream) return body.pipe(res); // body: json

body = JSON.stringify(body);

if (!res.headersSent) {

ctx.length = Buffer.byteLength(body);

}

res.end(body);

}

respond 方法里会根据外部是否有设置过 ctx.body,以及不同的 header 来设置 ctx.body,最终会调用 response.end 来结束掉本次请求。

注意到如果设置了 ctx.respond = false,这个方法就直接 return 了,这是一种跳过这里处理的方式。但其实如果我们在中间件中手动调用了 ctx.res.end() 后,相当于已经提前结束掉请求了,同样也不会走 Koa 这里的处理。

所以直接在中间件中调用 ctx.res.write()ctx.res.end() 就可以实现 chunked 类型的响应,倒无须对 Koa 做额外设置。

Koa 实现 chunked 数据传输

根据上面的分析,及之前一篇关于HTTP 响应的分块传输的文章,我们得出以下 Koa 中的实现逻辑:

const Koa = require("koa");
const app = new Koa();
const PORT = 3000;
app.use((ctx, _next) => {
const res = ctx.res;
ctx.status = 200;
res.setHeader("Content-Type", "text/html");
res.write(`start<br>`);
return new Promise(resolve => {
let i = 0,
total = 5;
while (i <= total) {
(function(i) {
setTimeout(() => {
if (i === total) {
resolve();
res.end();
} else {
res.write(`${i}<br>`);
}
}, i * 1000);
})(i);
i++;
}
});
}); app.listen(PORT);

console.info(</span>server started at http://localhost:<span class="pl-s1"><span class="pl-pse">${</span><span class="pl-c1">PORT</span><span class="pl-pse">}</span></span><span class="pl-pds">);

运行效果:

Koa 中实现 chunked 响应的运行效果

如你所见,Koa 中的这个实现会在调用 ctx.res.end() 后将本来应该在页面内容中处于最顶部的内容,移动到最底部。不解。

或者通过 curl 在命令行中查看效果:

$ curl -N http://localhost:3000

命令行中接收 chunked 数据的效果

示例代码可在 wayou/koa-chunked-response 找到。

相关资源

Koa 中实现 chunked 数据传输的更多相关文章

  1. iOS中关于动态Tableview中的cell数据传输的多线程问题解决之拙见

    iOS中关于动态Tableview中的cell数据传输的多线程问题解决之拙见 (2015-12-05 12:48:20)[编辑][删除] 转载▼     首先我们先明确一下问题: 1.因为UI是在主线 ...

  2. Koa与Node.js开发实战(3)——Nunjucks模板在Koa中的应用(视频演示)

    技术架构: ​ 在Koa中应用Nunjucks,需要先把Nunjucks集成为符合Koa规格的中间件(Middleware),从本质上来讲,集成后的中间件的作用是给上下文对象绑定一个render(vi ...

  3. Koa 中 ejs 模板的使用

    ejs的基本使用 安装 koa-views 和 ejs npm install --save koa-views/cnpm install --save koa-views npm install e ...

  4. 从前端中的IOC理念理解koa中的app.use()

    忙里偷闲,打开平时关注的前端相关的网站,浏览最近最新的前端动态.佼佼者,平凡的我做不到,但还是要争取不做落后者. 前端中的IoC理念,看到这个标题就被吸引了.IoC 理念,不认识呢,点击去一看,果然没 ...

  5. koa中返回404并且刷新后才正常的解决方案

    概述 这几天学习koa2,有一些心得,记录下来,供以后开发时参考,相信对其他人也有用. 起因 这几天学习koa2,写的代码执行时有一个奇怪的bug:明明能够返回数据,却有时正常返回数据,有时偏偏给你返 ...

  6. koa 基础(十六)koa 中 session 的使用

    1.app.js /** * koa 中 session 的使用 * 1.npm install koa-session --save * 2.const session = require('koa ...

  7. koa 基础(十一)koa 中 koa-bodyparser 中间件获取表单提交的数据

    1.app.js /** * koa 中 koa-bodyparser 中间件获取表单提交的数据 * 1.npm install --save koa-bodyparser * 2.引入 const ...

  8. koa 基础(十)原生node.js 在 koa 中获取表单提交的数据

    1.app.js // 引入模块 const Koa = require('koa'); const router = require('koa-router')(); /*引入是实例化路由 推荐*/ ...

  9. Cookie、Session、JWT在koa中的应用及实现原理

    目录 Cookie 重要属性 实现原理 cookie签名实现原理 注意事项 Session 实现原理 JWT 使用方式 组成 实际应用 实现原理 前端存储方式 cookie session local ...

随机推荐

  1. for 循环 乘法口诀表

    用for循环写乘法口诀表: for(var i = 1; i <= 9; i++) { var c=''; for(var x = 1; x <= i; x++) {    c=c+x+' ...

  2. css属性代码大全总结(一)

    一 CSS文字属性: color : #999999; /*文字颜色*/ font-family : 宋体,sans-serif; /*文字字体*/ font-size : 9pt; /*文字大小*/ ...

  3. 针对Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1的解决方案

    背景:本项目使用JDK1.8 编译maven工程的时候出现如下错误: Failed to execute goal org.apache.maven.plugins:maven-compiler-pl ...

  4. LA 4327

    Panagola, The Lord of city F likes to parade very much. He always inspects his city in his car and e ...

  5. 实验吧writeup

    后台登录 1.看源码有这样一段php代码<!-- $password=$_POST['password']; $sql = "SELECT * FROM admin WHERE use ...

  6. ubuntu 16.04 忘记登录密码的解决办法

    1.开机点击ESC或长按Shift,进入GUN GRUB界面  2.选择有recovery mode的选项,按e进入命令行  3.找到有recovery nomodeset的行,删除recovery ...

  7. 【ABCD组】Scrum meeting 4

    前言 第4次会议在6月16日由组长在教9 405召开. 主要对下一步的工作进行说明安排,时长90min. 主要内容 分配下阶段任务,争取在这阶段完成软件的设计阶段 任务分配 姓名 当前阶段任务 贡献时 ...

  8. 2013年工作中遇到的20个问题(Bug):161-180

    161.用户表和超级用户分成2个表,很不合理,查询的时候,非常复杂. 162.left join还是很有"市场"的.机构表Org连接User时,想获得user的名字,可能存在,也可 ...

  9. Oracle关联查询-数据类型不一致问题 ORA-01722: 无效数字

    一.存在表A和表B,都包含字段user_no,但数据类型不一致,如下: create table A ( user_id varchar2(20), user_no number(12,0), xxx ...

  10. Spring MVC灵活控制返回json的值(自定义过滤字段)

    在使用spring MVC开发过程中,为了提高项目执行效率,所以在一些外键字段的实体中会注解”@ManyToOne(fetch = FetchType.LAZY)”以实现延迟加载的效果. 但是,在使用 ...