Express.js文件上传:60秒超时和HTTP连接超时错误

4
我会很快解释一下我的应用程序的功能:
1. 客户上传文档 2. 文档被处理 3. 服务器创建pdf的快照图像 4. 服务器返回快照图像的路径
现在的问题是,从步骤(1)到(4)的整个过程需要超过60秒。
起初,我使用了旧的express.bodyParser()代码,但是在60秒后我会收到以下错误消息:
Error: Request aborted
    at IncomingMessage.onReqAborted (/DIR_TO_NODE/node/file-upload-error/node_modules/express/node_modules/connect/node_modules/multiparty/index.js:131:17)
    at IncomingMessage.EventEmitter.emit (events.js:92:17)
    at abortIncoming (http.js:1911:11)
    at Socket.serverSocketCloseListener (http.js:1923:5)
    at Socket.EventEmitter.emit (events.js:117:20)
    at TCP.close (net.js:466:12)

并且操作系统的tmp目录中的文件被删除了。

所以我决定使用mulster中间件模块来替代express.multipart()

这个改变解决了问题的一半,因为它消除了之前我遇到的Express.js多部分错误。

这意味着现在我的程序在60秒后仍然继续运行,这是好事,但是在同样的60秒后,客户端仍然会出现HTTP超时。

现在我知道像Chrome这样的浏览器有一个固定的60秒超时修复,但是我的问题是:

  • 有没有办法防止在60秒后发生HTTP超时?

  • 如果没有办法,其他应用程序是如何解决这个问题的?我特别关注同时有多个用户争夺文件系统访问权限的情况。

下面是一些测试案例,我通过从不使用res.send()回答请求并每10秒轮询tmp目录中的文件名来模拟长时间处理,只是为了检查我的文件何时会消失(有时是60秒超时,有时是120秒超时)。

测试用例编号1 - 使用传统的express.bodyParser()中间件:

var express = require('express');
var path = require('path');
var app = express();
var fs = require('fs');
var os = require('os');

var pid = process.pid.toString();
var interval;

app.use(express.bodyParser());

app.get('/', function (req, res) {
    res.send([
        '<form method="post" enctype="multipart/form-data">' +
            '<p>Image: <input type="file" name="pdf" /></p>' +
            '<p><input type="submit" value="Upload" /></p>' +
        '</form>'
    ].join(''));
});

app.post('/', function (req, res, next) {

    var file_path;
    if(req.files.pdf) {
        file_path = req.files.pdf.path;
    }
    else {
        file_path = os.tmpDir() + 'test';
    }

    var filename = path.basename(file_path);
    var dir = path.dirname(file_path);

    console.log('\n> PID: ' + pid);
    console.log('> Filepath: ' + dir);
    console.log('> Filename: ' + filename);

    var count = 0;
    clearInterval(interval);
    interval = setInterval(function () {
        count++;
        console.log('\n> Interval: ' + (count * 10) + ' seconds');

        var files = fs.readdirSync(dir);
        for(var i = 0; i < files.length; i++) {
            if(files[i].substr(0, pid.length) === pid) {
                console.log('\t-> File present: ' + files[i]);
            }
        }

    }, 10000);

});

app.listen(3000);
console.log('Express started on port 3000');

测试用例2 - 使用mulster中间件:
var express = require('express');
var multer = require('multer');
var path = require('path');
var app = express();
var fs = require('fs');
var os = require('os');

var pid = process.pid.toString();
var interval;

app.use(express.json());
app.use(express.urlencoded());
app.use(multer({
    dest: os.tmpDir()
}));

app.get('/', function (req, res) {
    res.send([
        '<form method="post" enctype="multipart/form-data">' +
            '<p>Image: <input type="file" name="pdf" /></p>' +
            '<p><input type="submit" value="Upload" /></p>' +
        '</form>'
    ].join(''));
});

app.post('/', function (req, res, next) {

    var file_path;
    if(req.files.pdf) {
        file_path = req.files.pdf.path;
    }
    else {
        file_path = os.tmpDir() + 'test';
    }

    //var file_path = req.files.pdf.path;
    var filename = path.basename(file_path);
    var dir = path.dirname(file_path);

    console.log('\n> PID: ' + pid);
    console.log('> Filepath: ' + dir);
    console.log('> Filename: ' + filename);

    var count = 0;
    clearInterval(interval);
    interval = setInterval(function () {
        count++;
        console.log('\n> Interval: ' + (count * 10) + ' seconds');

        var files = fs.readdirSync(dir);
        for(var i = 0; i < files.length; i++) {
            if(files[i].split('.')[0].length === 32 || files[i].substr(0, pid.length) === pid) {
                console.log('\t-> File present: ' + files[i]);
            }
        }

    }, 10000);

});

app.listen(3000);
console.log('Express started on port 3000');

你解决了这个问题吗?我在这里得到了完全相同的错误。我已经花了几天时间来解决这个问题,但还没有任何进展 :-( - teleme.io
我也是,你有找到什么东西吗? - httpete
@httpete 不,唯一的解决方案是在整个文件上传完成后立即返回HTTP 200。文件本身的处理是在请求已经完成之后进行的。由于我的应用程序已经出于其他原因使用了Websockets,所以处理进度%s是通过它们推送到客户端的UI。 - m_vdbeek
2个回答

5
这个问题看起来与这个问题非常相似:这个问题。60秒的超时很可能来自底层套接字,可以通过以下方式解决:

app.post('/', function (req, res, next) { {
    // 10 minute timeout just for POST to /
    req.socket.setTimeout(10 * 60 * 1000);
    
    // ...

});


它有效了。这正是我在寻找的,只需要这一行代码req.socket.setTimeout(10 * 60 * 1000);,谢谢。 - Francesco Orsi

0
据我所见(在这两种情况下),您都有一个用于处理POST请求的路由处理程序,但是在该处理程序中,您没有发送响应。您应该在那里也使用res.send。否则用户将根本不会收到响应。

检查我的更新。我故意没有发送 res.send() 以模拟长时间处理。 - m_vdbeek
这听起来像是一个非常糟糕的想法。你应该始终返回响应。否则用户将看到一片空白的页面。 - Krasimir
3
我觉得你可能没有理解我的意思。我有一个实际处理需要超过60秒的应用程序,但是为了说明问题,我创建了一个更简短的示例来模拟我的问题。 - m_vdbeek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接