如何在Formidable(Node.js)中取消用户上传?

jfi*_*izz 12 javascript file-upload node.js express

我已经在这个问题上工作了两天了,我被卡住了.我正在使用带有Express的Node.js,我正在尝试实现上传表单.基本上,我希望表单执行以下操作:

  • 检查文件的大小,如果文件过大则取消上传(当我说取消时,我的意思是阻止任何进一步的数据写入磁盘并删除临时文件)

  • 检查文件类型并确认它是正确的类型(.jpg,.png等),如果不是,则停止进一步写入磁盘并删除临时文件.

目前,我有上传工作,当文件太大或与正确类型不匹配时我发出错误,然后fs.unlink()在整个文件写入磁盘后删除文件.但是我发现这种方法存在一个潜在的问题:如果用户上传了一个巨大的文件(GB的大小)怎么办?凭借我的方法,它最终将从我的机器中删除,但不会浪费大量资源.基本上,我希望使用最少量的资源来确认文件可以上传.这是我到目前为止的代码:

    var path = absolutePath + '/public/images/users/' + req.session.userId + '/';
    var maxSize = 3146000; // 3MB
    var form = new formidable.IncomingForm();
    form.uploadDir = path;
    form.keepExtensions = true;

    form.on('error', function(message) {
        if(message)
        {
            res.json({err: message});
        }
        else
        {
            res.json({err: 'Upload error, please try again'});
        }
    });

    form.on('fileBegin', function(name, file){
        if(form.bytesExpected > maxSize)
        {
            this.emit('error', 'Size must not be over 3MB');
        }
    });

    form.on('file', function(name, file) {
        var type = file.type;
        type = type.split('/');
        type = type[1];

        if(type != 'jpeg' && type != 'png' && type != 'gif')
        {
            this.emit('error', "JPG's, PNG's, GIF's only");
            fs.unlink(file.path);
        }
        else
        {
            fs.rename(file.path, path + 'profile.' + type);
        }
    });

    form.on('progress', function(bytesReceived, bytesExpected) {
            console.log(bytesReceived); //This is just to view progress
    });

    form.parse(req);
Run Code Online (Sandbox Code Playgroud)

我也很困惑,因为根据https://github.com/felixge/node-formidable上的文件,它说:

遇到错误的请求会自动暂停,如果您希望请求继续触发"数据"事件,则必须手动调用request.resume().

这将是伟大的,但我似乎无法让它工作.每当我发出"错误"时,"数据"事件会一直持续到完成.

尝试

我已经尝试在发生错误时取消请求,但无济于事.req.pause()什么也没做对我来说,req.end()req.abort()给了我一个错误说,这是不是一种方法,并req.connection.destroy()req.connection.end()刚送POST请求的循环.

最后的想法

所以我正在寻找它似乎应该是常见的,但我花了最后两天在互联网上搜索彻底的实施,我似乎找不到任何东西.我的意思是,在整个事情上传之后检查文件的大小和类型很简单,但是谁想要浪费所有这些资源?更不用说恶意用户可以做的事情了.

我将继续工作,直到我得到我正在寻找的东西,但我认为这个问题可能与其他一些用户有关,希望我能得到一些帮助!

谢谢你的时间.

jfi*_*izz 8

我会尝试回答我自己的问题......

因此,在使用Formidable进行一些试验和错误之后,我只是放弃了它并切换到了Multiparty.当发出错误时,多方实际取消上传.

我的解决方案

所以我的解决方案利用客户端大小和类型检查(未在代码中显示).然后将请求发送到服务器.在服务器上,我再次检查文件大小和类型是否正确在写入磁盘之前.我可以通过使用Multiparty的part活动来做到这一点.如果它们不正确,那么我只是发送一个413错误的响应.(感谢josh3736澄清了浏览器中应该发生的事情.)在发回413之后,浏览器的行为有点零星.对于我正在测试的浏览器,它只显示了一个pending帖子请求.我认为这种行为是由于整个表单尚未处理,因此,它不会接受任何回复.这似乎不是处理它的最优雅方式,因为没有显示错误代码,但这种行为只会被绕过客户端检查的恶意用户遇到(我的网站依赖于Javascript,所以所有用户都会拥有它如果他们想要使用我的网站,则启用).所以这是我的解决方案,现在是一些代码......

app.post('/profile/profile_pic', urlencoded, function (req, res) {

    var path = absolutePath + '/public/images/users/' + req.session.userId + '/';
    var maxSize = 3146000; // 3MB

    var options = {uploadDir: path};
    var form = new multiparty.Form();

    form.on('error', function(message) {
        res.status = 413;
        res.send(413, 'Upload too large');
        res.end();
    });

    form.on('file', function(name, file) {
        var type = file.headers['content-type'];
        type = type.split('/');
        type = type[1];
        fs.rename(file.path, path + 'profile.' + type);
        path = '/images/users/' + req.session.userId + '/profile.' + type;
    });

    form.on('part', function(part) {
        var type = part.headers['content-type'];
        var size = part.byteCount - part.byteOffset;

        if(type != 'image/jpeg' && type != 'image/png' && type != 'image/gif' != 'application/pdf' || size > maxSize)
        {
            this.emit('error');
        }
    });

    form.on('close', function(){
        res.json({err: 0, path: path});
    });

    form.parse(req);

});
Run Code Online (Sandbox Code Playgroud)


jos*_*736 6

要中止上传,正确的做法是关闭套接字.

req.socket.end();
Run Code Online (Sandbox Code Playgroud)

不幸的是,服务器想要中止正在进行的HTTP上传的情况是一团糟.

在这里做正确的,符合规范的事情就是尽早发送HTTP 413响应 - 也就是说,一旦检测到客户端发送的字节数多于您想要处理的字节数.在发送错误响应后是否终止套接字取决于您.这符合RFC 2616. [...]接下来发生的事情并不理想.

  • 如果您打开套接字,所有浏览器(Chrome 30,IE 10,Firefox 21)将继续发送数据,直到整个文件上传为止.然后,只有这样,浏览器才会显示您的错误消息.这真的很糟糕,因为用户必须等待整个文件完成上传,才发现服务器拒绝了它.它也浪费你的带宽.

    浏览器的当前行为违反了RFC2616§8.2.2:

    发送消息体的HTTP/1.1(或更高版本)客户端应该在发送请求时监视网络连接的错误状态.如果客户端看到错误状态,它应该立即停止传输正文.如果正在使用"分块"编码发送正文(第3.6节),则可以使用零长度块和空拖车来过早标记消息的结尾.如果正文前面有Content-Length标头,则客户端必须关闭连接.

    开放的ChromeFirefox问题,但不要指望很快修复.

  • 如果您在发送HTTP 413响应后立即关闭套接字,所有浏览器显然会立即停止上传,但它们当前显示"连接重置"错误(或类似),而不是您可能在响应中发送的任何HTML.

    同样,这可能违反了规范(允许服务器提前发送响应并关闭连接),但我不希望浏览器在这里很快修复.

你看到一系列POST请求的事实是可疑的.你在使用某种AJAX上传器吗?在提前关闭套接字后,它可能会自动重试上载.

  • 对不起,忘了提到你必须将`Connection`标题设置为`close`.`res.header('Connection','close'); res.send(413,'Upload too large');`节点将自动关闭套接字,终止上传. (3认同)
  • 不要'破坏',发送HTTP 413和`end`. (2认同)

Eug*_*ala 1

一段时间过去了,现在您可以使用multer而不是强大或多方。Multer 内置了所需的功能。