2016-03-15 64 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); 
}); 
相關問題