2016-03-15 102 views
0

我试着拉上一堆文件并将其上传到远程服务器(这也是写的NodeJS和使用表单处理多方)上传文件,而无需使用请求模块临时文件的NodeJS

,但我想要的要做到这一点就是不用临时文件。这意味着“:创建一个zip压缩包的流,并直接通过request模块上传流

,所以我写了一些测试代码,首先是接收上传服务器:

var multiparty = require('multiparty'); 
var http = require('http'); 
var util = require('util'); 

http.createServer(function(req, res) { 
    if (req.url === '/upload' && req.method === 'POST') { 
    // parse a file upload 
    var form = new multiparty.Form({encoding: null}); 
    console.log("what?"); 
    form.parse(req, function(err, fields, files) { 
     if (err) { 
     console.error(err); 
     } 

     res.writeHead(200, {'content-type': 'text/plain'}); 
     res.write('received upload:\n\n'); 
     res.end(util.inspect({fields: fields, files: files})); 
    }); 

    return; 
    } 

    // show a file upload form 
    res.writeHead(200, {'content-type': 'text/html'}); 
    res.end(
    '<form action="/upload" enctype="multipart/form-data" method="post">'+ 
    '<input type="text" name="title"><br>'+ 
    '<input type="file" name="upload" multiple="multiple"><br>'+ 
    '<input type="submit" value="Upload">'+ 
    '</form>' 
); 
}).listen(8080); 

然后我只是用child_process执行zip命令,但不是创建一个临时文件,我输出存档,以便标准输出来创建流:

/* 
    upload a manually-created readable stream from string output from zip command. 
*/ 
var exec = require('child_process').exec; 
var dir = '/Users/drakedan/Documents/screenshot/1457950544039/'; 

var zipCmd = 'zip -r9 - ' + dir; 

var Stream = require('stream'); 
var request = require('request'); 
var fs = require('fs'); 
exec(zipCmd, {encoding: null}, function(err, stdout, stderr) { 
    if (err) console.error(err); 

    var rs = Stream.Readable({encoding: null}); 

    rs.push(stdout); 
    rs.push(null); 

    var req = request.post('http://127.0.0.1:8080/upload', function(err, httpResponse, body) { 
     if (err) console.error(err); 
     console.log(body); 
    }); 
    var form = req.form(); 
    form.append('image', rs); 

}) 

但我跑得后上传脚本,服务器返回

{ [Error: stream ended unexpectedly] status: 400, statusCode: 400 }

只是好像有什么不对的可读流。

为了检查该流内容是否有损坏。我将脚本修改为使用临时文件。

/* 
    first write the stream content to a temp file, then 
    upload a new stream from the temp file. 
*/ 
var exec = require('child_process').exec; 
var dir = '/Users/drakedan/Documents/screenshot/1457950544039/'; 

var zipCmd = 'zip -r9 - ' + dir; 

var Stream = require('stream'); 
var request = require('request'); 
var fs = require('fs'); 
exec(zipCmd, {encoding: null}, function(err, stdout, stderr) { 
    if (err) console.error(err); 

    var rs = Stream.Readable({encoding: null}); 

    rs.push(stdout); 
    rs.push(null); 


    var ws = fs.createWriteStream('/Users/drakedan/Desktop/test.zip'); 
    rs.pipe(ws);// create a temp file. 

    rs.on('end', function(){ 
     var req = request.post('http://127.0.0.1:8080/upload', function(err, httpResponse, body) { 
      if (err) console.error(err); 
      console.log(body); 
     }); 
     var form = req.form(); 
     form.append('image', fs.createReadStream('/Users/drakedan/Desktop/test.zip')); // readstream from temp file 
    }); 

}); 

这次,服务器接受该文件。

received upload: 

{ fields: {}, files: { image: [ [Object] ] } } 

并且临时文件可以被成功解压缩,这意味着没有内容损坏。

所以我的问题是:

什么是处理这种情况的正确方法?

fs.createReadStream和我手动创建的可读流之间有什么区别。

我有什么机会可以流过整个过程?

我做错了什么?

回答

0

经过深入挖掘,我想我找到了解决问题的方法。

stream ended unexpectedly基于我的假设错误,主要是因为服务器端并不知道流实际结束于哪里。说文件的大小是未知的。我从这个blog得到了一些提示,它改变了transfer-encoding:'chunked'标题。在我做同样的事情之后,服务器似乎接受了表单,而不是作为上传的文件,而是作为普通的字段。

这是一个进步!然后我再次访问form-datarequest我终于找出了正确的方法。

有2种方式来解决这个问题:

  1. 使用自定义窗体选项构建FORMDATA,把标准输出直接作为内容。给它一个正确的内容类型然后提交它,并完成它。

    var exec = require('child_process').exec; 
    var dir = '/Users/drakedan/Documents/screenshot/1457950544039/'; 
    
    var zipCmd = 'zip -r9 - ' + dir; 
    
    var Stream = require('stream'); 
    var request = require('request'); 
    var fs = require('fs'); 
    var FormData = require('form-data'); 
    exec(zipCmd, {encoding: null}, function(err, stdout, stderr) { 
        if (err) console.error(err); 
    
        var form = new FormData(); 
        form.append('image', stdout, { 
         filename: 'upload.zip', 
         contentType: 'application/zip, application/octet-stream' 
        }); 
    
        form.submit('http://127.0.0.1:8080/upload', function(err, res) { 
         if (err) throw err; 
         console.log(res); 
        }); 
    }); 
    

2,采用手动创建可读流。尽管在这种情况下似乎不需要 ,但仍然很好知道。关键点是定义已知长度,否则会发生stream ended unexpectedly

var exec = require('child_process').exec; 
var dir = '/Users/drakedan/Documents/screenshot/1457950544039/'; 

var zipCmd = 'zip -r9 - ' + dir; 

var Stream = require('stream'); 
var request = require('request'); 
var fs = require('fs'); 
var FormData = require('form-data'); 
exec(zipCmd, {encoding: null}, function(err, stdout, stderr) { 
    if (err) console.error(err); 

    var rs = Stream.Readable({encoding: null}); 

    rs.push(stdout); 
    rs.push(null); 

    var form = new FormData(); 
    form.append('image', rs, { 
     filename: 'upload.zip', 
     contentType: 'application/zip, application/octet-stream', 
     knownLength: 116089 // necessary if using custom steam 
    }); 

    form.submit('http://127.0.0.1:8080/upload', function(err, res) { 
     if (err) throw err; 
     console.log('done'); 
    }); 
}); 

通过了解发生了什么事,我可以用request获得完成工作只是更改代码。使用自定义文件选项

var formData = { 
    image: { 
     value: stdout, 
     options: { 
      filename: 'upload.zip', 
      contentType: 'application/zip, application/octet-stream' 
     } 
    } 
} 

var req = request.post({url: 'http://127.0.0.1:8080/upload', formData: formData}, function(err, httpResponse, body) { 
    if (err) console.error(err); 
    console.log(body); 
}); 
相关问题