2

是否可以编写非阻塞response.write?我编写了一个简单的测试来查看其他客户端是否可以在下载文件时连接:

var connect = require('connect');

var longString = 'a';
for (var i = 0; i < 29; i++) { // 512 MiB
   longString += longString;
}
console.log(longString.length)

function download(request, response) {
    response.setHeader("Content-Length", longString.length);
    response.setHeader("Content-Type", "application/force-download");
    response.setHeader("Content-Disposition", 'attachment; filename="file"');
    response.write(longString);
    response.end();
}

var app = connect().use(download);
connect.createServer(app).listen(80);

而且好像write在阻塞!

难道我做错了什么?

更新所以,它不会阻塞,它会同时阻塞。从可以同时下载两个文件的意义上说,它不会阻塞。从某种意义上说,它会阻塞,因为创建缓冲区是一个漫长的操作。

4

2 回答 2

2

任何严格在 JavaScript 中完成的处理都会阻塞。response.write(),至少从 v0.8 开始,也不例外

第一次response.write()被调用,它会将缓冲的头部信息和第一个正文发送给客户端。第二次response.write()调用时,Node 假设您将要流式传输数据,并单独发送。也就是说,响应被缓冲到正文的第一块。

true如果整个数据已成功刷新到内核缓冲区,则返回。false如果全部或部分数据在用户内存中排队,则返回。'drain'当缓冲区再次空闲时将发出。

可以节省一些时间的是在尝试之前转换longString为,因为无论如何都会发生转换:Bufferwrite()

var longString = 'a';
for (...) { ... }
longString = new Buffer(longString);

但是,最好是流式传输各个块longString而不是一次流式传输(注意:流在 v0.10 中发生了变化):

var longString = 'a',
    chunkCount = Math.pow(2, 29),
    bufferSize = Buffer.byteLength(longString),
    longBuffer = new Buffer(longString);

function download(request, response) {
    var current = 0;

    response.setHeader("Content-Length", bufferSize * chunkCount);
    response.setHeader("Content-Type", "application/force-download");
    response.setHeader("Content-Disposition", 'attachment; filename="file"');

    function writeChunk() {
        if (current < chunkCount) {
            current++;

            if (response.write(longBuffer)) {
                process.nextTick(writeChunk);
            } else {
                response.once('drain', writeChunk);
            }
        } else {
            response.end();
        }
    }

    writeChunk();
}

fs.createReadStream()而且,如果最终目标是从磁盘流式传输文件,使用and可以更容易stream.pipe()

function download(request, response) {
    // response.setHeader(...)
    // ...

    fs.createReadStream('./file-on-disk').pipe(response);
}
于 2013-02-11T19:27:25.187 回答
1

不,它不会阻止,我从 IE 尝试了一个,从 firefox 尝试了另一个。我先做了IE,但仍然可以先从firefox下载文件。我尝试了 1 MB (i < 20),它的工作速度更快。您应该知道,您创建的任何 longString 都需要内存分配。尝试为 i < 30(在 Windows 7 上)执行此操作,它会抛出 FATAL ERROR: JS Allocation failed - process out of memory。

内存分配/复制需要时间。由于它是一个巨大的文件,因此响应需要时间,并且您的下载看起来像是被阻塞了。自己尝试较小的值(i < 20 或其他值)

于 2013-02-11T18:40:09.027 回答