2014-12-03 3 views
1

우리는 sails.js를 사용하여 앱을 개발 중입니다. 문서에 설명 된대로, 후드
https://github.com/makinacorpus/mnhn_bai/blob/master/api/controllers/Object3DController.jsnginx/sails.js : 불완전한 파일 업로드

이 컨트롤러를 사용 선장 :이 응용 프로그램에서 우리는 업로드 컨트롤러가 있습니다.

큰 파일을 업로드 할 때 불완전하게 저장되는 문제는 업로드 된 크기가 결코 같지 않으며 15MB 파일의 경우 7MB에서 14MB까지 다양하다는 것입니다.

아키텍처는 다음과 같습니다.
haproxy -> nginx -> node.js/sails.

단순한 apache + proxypass 구성으로 nginx 역방향 프록시를 대체하면 업로드가 완벽하게 작동합니다.

node.js 앱을 간단한 파이썬 업로드 컨트롤러 (예 : 플라스크)로 바꾸면 업로드시 올바른 길이와 데이터도 표시됩니다.

물론 nginx는 buffer_client_body_timeout과 client_max_body_size에 대해 올바르게 구성되어 있습니다. 그리고 말했듯이 플라스크는 올바르게 업로드를 수신하고 있습니다.

nginx 앱으로 업로드하면 파일이 업로드 된 것처럼 보이지만 사실 디스크에 파일이 없으면 파일이 불완전 해집니다.

2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header: 
"POST /admin/edit_object/6 HTTP/1.1^M 
Host: xxxxxx.makina-corpus.net^M 
X-Real-IP: xxxx^M 
X-Forwarded-For: xxxxx^M 
X-NginX-Proxy: true^M 
X-Forwarded-Proto: http^M 
Connection: upgrade^M 
Content-Length: 15361775^M 
Origin: http://bai.makina-corpus.net^M 
User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) CasperJS/1.1.0-beta3+PhantomJS/1.9.8 Safari/534.34^M 
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryRt4v4f7RkrlzUEX2^M 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8^M 
Referer: http://xxxxxxxxxx.makina-corpus.net/admin/edit_object/6^M 
Cookie: sails.sid=s%3Akv_Gxxxxxxxx2F5iaDWA^M 
Accept-Encoding: gzip^M 
Accept-Language: en,*^M 
Authorization: Basic xxxx=^M 
^M 
" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http cleanup add: 00000000011CC520 
2014/12/03 01:57:23 [debug] 39583#0: *1 init keepalive peer 
2014/12/03 01:57:23 [debug] 39583#0: *1 get keepalive peer 
2014/12/03 01:57:23 [debug] 39583#0: *1 get rr peer, try: 1 
2014/12/03 01:57:23 [debug] 39583#0: *1 get keepalive peer: using connection 0000000001156018 
2014/12/03 01:57:23 [debug] 39583#0: *1 http upstream connect: -4 
2014/12/03 01:57:23 [debug] 39583#0: *1 http upstream send request 
2014/12/03 01:57:23 [debug] 39583#0: *1 chain writer buf fl:0 s:806 
2014/12/03 01:57:23 [debug] 39583#0: *1 chain writer buf fl:1 s:15361775 
2014/12/03 01:57:23 [debug] 39583#0: *1 chain writer in: 00000000011CC5C0 
2014/12/03 01:57:23 [debug] 39583#0: *1 tcp_nopush 
2014/12/03 01:57:23 [debug] 39583#0: *1 writev: 806 
2014/12/03 01:57:23 [debug] 39583#0: *1 sendfile: @0 15361775 
2014/12/03 01:57:23 [debug] 39583#0: *1 sendfile: 2776864, @0 2776864:15361775 
2014/12/03 01:57:23 [debug] 39583#0: *1 chain writer out: 00000000011CC5D0 
2014/12/03 01:57:23 [debug] 39583#0: *1 event timer add: 35: 60000:1417568303245 
2014/12/03 01:57:23 [debug] 39583#0: *1 http run request: "/admin/edit_object/6?" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http request empty handler 
2014/12/03 01:57:23 [debug] 39583#0: *1 http upstream request: "/admin/edit_object/6?" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http upstream send request handler 
2014/12/03 01:57:23 [debug] 39583#0: *1 http upstream send request 
2014/12/03 01:57:23 [debug] 39583#0: *1 chain writer in: 00000000011CC5D0 
2014/12/03 01:57:23 [debug] 39583#0: *1 sendfile: @2776864 12584911 
2014/12/03 01:57:23 [debug] 39583#0: *1 sendfile: 2488810, @2776864 2488810:12584911 
2014/12/03 01:57:23 [debug] 39583#0: *1 chain writer out: 00000000011CC5D0 
2014/12/03 01:57:23 [debug] 39583#0: *1 event timer del: 35: 1417568303245 
2014/12/03 01:57:23 [debug] 39583#0: *1 event timer add: 35: 60000:1417568303254 
2014/12/03 01:57:23 [debug] 39583#0: *1 http upstream request: "/admin/edit_object/6?" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http upstream process header 
2014/12/03 01:57:23 [debug] 39583#0: *1 malloc: 00000000011CD000:262144 
2014/12/03 01:57:23 [debug] 39583#0: *1 recv: fd:35 369 of 262144 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy status 200 "200 OK" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header: "X-Powered-By: Sails <sailsjs.org>" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header: "Access-Control-Allow-Origin: " 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header: "Access-Control-Allow-Credentials: " 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header: "Access-Control-Allow-Methods: " 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header: "Access-Control-Allow-Headers: " 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header: "Content-Type: application/json; charset=utf-8" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header: "Content-Length: 33" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header: "Vary: Accept-Encoding" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header: "Date: Wed, 03 Dec 2014 00:57:23 GMT" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header: "Connection: keep-alive" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header done 
2014/12/03 01:57:23 [debug] 39583#0: *1 uploadprogress error-tracker error: 0 
2014/12/03 01:57:23 [debug] 39583#0: *1 xslt filter header 
2014/12/03 01:57:23 [debug] 39583#0: *1 HTTP/1.1 200 OK^M 
Server: nginx^M 
Date: Wed, 03 Dec 2014 00:57:23 GMT^M 

문제는 선장이 결코 상류 수준에서 무한 루프를 '마무리'이벤트를 칠 것 같다 것으로 보인다 : 우리가 볼 수있는 nginx를 디버그 로그에

? 돛은 표준 출력

Parser: Done reading textparam through field `gallery` 
Parser: Done reading textparam through field `category` 
Parser: Done reading textparam through field `copyright` 
Parser: Done reading textparam through field `published` 
Parser: Done reading textparam through field `filename_3D` 
Parser: Done reading textparam through field `filename_flat` 
Parser: Done reading textparam through field `preview` 
Parser: Done reading textparam through field `preview_animated` 
Something is trying to read from Upstream `media_files`... 
Passing control to app... 
User allowed : admin (1) 
RenamerPump: 
• dirname => undefined 
• field => media_files 
• fd => 04cb80ba-dce6-4a1d-9b54-ac8b08ca3e06 
100 
100 
100 
100 
100 
100 
100 
100 
100 
100 
100 

흥미로운 것은 서로 관련이없는 요청의 헤더 업로드 후 디스크 파일의 끝 부분에 포함되어 있습니다 :

07bb890: 3130 3130 3130 3130 3130 3130 3130 3130 1010101010101010 
07bb8a0: 3130 3130 3130 3130 3130 3130 3130 3130 1010101010101010 
07bb8b0: 3130 3130 3130 3130 3130 3130 3130 3130 1010101010101010 
07bb8c0: 3130 3130 3130 3130 3130 3130 4745 5420 101010101010GET 
07bb8d0: 2f20 4854 5450 2f31 2e31 0d0a 486f 7374/HTTP/1.1..Host 
07bb8e0: xxxx xxxx xxxx xxxx xxxx xxxx 2d63 6f72 : xxx.makina-cor 
07bb8f0: 7075 732e 6e65 740d 0a58 2d52 6561 6c2d pus.net..X-Real- 
07bb900: 4950 
07bb910: 3134 0d0a 582d 466f 7277 6172 6465 642d 14..X-Forwarded- 
07bb920: 466f          For: xxxxxxxxxxx 
07bb930: 2e31        
07bb940: 2e31 340d 0a58 2d4e 6769 6e58 2d50 726f .14..X-NginX-Pro 
07bb950: 7879 3a20 7472 7565 0d0a 582d 466f 7277 xy: true..X-Forw 
07bb960: 6172 6465 642d 5072 6f74 6f3a 2068 7474 arded-Proto: htt 
07bb970: 700d 0a43 6f6e 6e65 6374 696f 6e3a 2075 p..Connection: u 
07bb980: 7067 7261 6465 0d0a 5573 6572 2d41 6765 pgrade..User-Age 
07bb990: 6e74 3a20 4d6f 7a69 6c6c 612f 352e 3020 nt: Mozilla/5.0 
07bb9a0: 2855 6e6b 6e6f 776e 3b20 4c69 6e75 7820 (Unknown; Linux 
07bb9b0: 7838 365f 3634 2920 4170 706c 6557 6562 x86_64) AppleWeb 
07bb9c0: 4b69 742f 3533 342e 3334 2028 4b48 544d Kit/534.34 (KHTM 
07bb9d0: 4c2c 206c 696b 6520 4765 636b 6f29 2043 L, like Gecko) C 
07bb9e0: 6173 7065 724a 532f 312e 312e 302d 6265 asperJS/1.1.0-be 
07bb9f0: 7461 332b 5068 616e 746f 6d4a 532f 312e ta3+PhantomJS/1. 
07bba00: 392e 3820 5361 6661 7269 2f35 3334 2e33 9.8 Safari/534.3 
07bba10: 340d 0a41 6363 6570 743a 2074 6578 742f 4..Accept: text/ 
07bba20: 6874 6d6c 2c61 7070 6c69 6361 7469 6f6e html,application 
07bba30: 2f78 6874 6d6c 2b78 6d6c 2c61 7070 6c69 /xhtml+xml,appli 
07bba40: 6361 7469 6f6e 2f78 6d6c 3b71 3d30 2e39 cation/xml;q=0.9 
07bba50: 2c2a 2f2a 3b71 3d30 2e38 0d0a 4163 6365 ,*/*;q=0.8..Acce 
07bba60: 7074 2d45 6e63 6f64 696e 673a 2067 7a69 pt-Encoding: gzi 
07bba70: 700d 0a41 6363 6570 742d 4c61 6e67 7561 p..Accept-Langua 
07bba80: 6765 3a20 656e 2c2a 0d0a 4175 7468 6f72 ge: en,*..Author 
07bba90: 697a 6174 696f 6e3a 2042 6173 6963 2063 ization: Basic c 
07bbaa0: 6d39 xxxx xxxx xxxx xxxx 3d0d 0a   xxxx=.. 
(END) 

그리고 다른 요청에

, 우리가하지 일부가 다른 요청 헤더지만, 불완전한 파일. 여기서 누락 된 비트는 원본 파일의 끝에서부터 시작하며 항상 시작이 올바른 것입니다.

아파치와 가장 큰 차이점은 nginx가 돛 응용 프로그램에 데이터를 빠르게 보내고 있다는 것입니다. 반대로 아파치는 요청을 스트리밍합니다. 이것은 nginx가 버퍼링을 요청하기 때문입니다.

누가 업로드 문제를 파헤 치기 위해 선장에서 계속해야하는지 아이디어가 있다면! 내가이 예에서 방법을 저장 교체 할 경우

, 난 오류가 선장 요청 소비에 분명 어딘가에 내가, 게시 된 데이터의 완전하고 올바른 파일을 가지고 nginx를에서 나오는 비트가 정확하게 기록 된 것을 볼 수

var body = ""; 
req.on('data', function (chunk) { 
    body += chunk; 
}); 
req.on('end', function() { 
    console.log('POSTed: ' + body.length); 
    console.log('POSTed: ' + body.slice(-400)); 
    res.writeHead(200); 
    res.end('<html/>'); 
}); 
+0

앱 'csrf'가 활성화되어 있습니까? 또한 헤더 나 요청 본문에'csrf'를 보냅니 까? – myusuf

답변

1

그래서, 제가 발견 한 해결책은 무시 무시한 것을 사용하는 바디 파서를 해킹하는 것입니다.

더 이상 문제가 없습니다.

설정/http.js

module.exports.http = { 
    middleware: { 
    bodyParser: false, 
    cbodyParser: require('../bodyParser')(
     {urls: [/\/admin\/edit_object/]}), 
    order: [ 
    'startRequestTimer', 
    'cookieParser', 
    'session', 
    'cbodyParser', 
    'handleBodyParserError', 
    'compress', 
    'methodOverride', 
    'poweredBy', 
    '$custom', 
    'router', 
    'www', 
    'favicon', 
    '404', 
    '500' 
    ],  
    } 
}; 

bodyparser.js :

/** 
* Module dependencies 
    // Configure body parser components 
*/ 
var _ = require('lodash'); 
var util = require('util'); 
var formidable = require('formidable'); 

function mime(req) { 
    var str = req.headers['content-type'] || ''; 
    return str.split(';')[0]; 
} 

function parseMultipart(req, res, next) { 
    req.form = new formidable.IncomingForm(); 
    req.form.uploadDir = sails.config.data.__uploadData; 
    req.form.maxFieldsSize = sails.config.maxsize; 
    req.form.multiple = true; 
    // res.setTimeout(0); 
    req.form.parse(req, function(err, fields, files) { 
    if (err) 
     return next(err); 
    else { 
     req.files = files; 
     req.fields = fields; 
     req.body = extend(fields, files); 
     next(); 
    } 
    }); 
} 

function extend(target) { 
    var key, obj; 
    for (var i = 1, l = arguments.length; i < l; i++) { 
    if ((obj = arguments[i])) { 
     for (key in obj) 
     target[key] = obj[key]; 
    } 
    } 
    return target; 
} 

function disable_parser(opts, req, res) { 
    var matched = false; 
    try { 
     var method = null; 
     try {method = req.method.toLowerCase();} 
     catch(err){ /* */} 
     if(method) { 
      _(opts.urls).forEach(function(turl) { 
       if (method === 'post' && req.url.match(turl)) { 
        // console.log("matched"+ req.url); 
        if(!matched) matched = true; 
       };}); 
     } 
    } catch(err) { debug(err);/* pass */ } 
    return matched; 
} 

module.exports = function toParseHTTPBody(options) { 
    options = options || {}; 
    var bodyparser = require('skipper')(options); 
    // NAME of anynonymous func IS IMPORTANT (same as the middleware in config) !!! 
    return function cbodyParser(req, res, next) { 
    var err_hdler = function(err) {}; 
    if (disable_parser(options, req, res) && mime(req) == 'multipart/form-data') { 
     return parseMultipart(req, res, next); 
    } else { 
     return bodyparser(req, res, next); 
    } 
    }; 
}; 

기록을 위해

, 그것은 해킹의 비트가 미들웨어에 bodyparser를 전환했다 실제로, 돛은 우리가 bodyParser를 오버라이드 할 수 있다고 생각하게하지만, 익명의 함수가 될 것이지만 익스프레스 라우터는 "명명 된"함수 만 매핑합니다 ...

0

우리도 비슷한 문제에 직면했습니다. 나는 우리의 솔루션이 당신을 위해 일할 것인지 아닌지는 모르지만, 여기에 있습니다.

매우 큰 파일의 경우 csrfrequest packet에서 제외됩니다. 따라서 request body 대신 request headercsrf을 보내야합니다.이를 위해 XMLHttpRequest을 약간 변경했습니다.

/* 
Putting csrf in Header as some large 
files need this mechanism to upload 
*/ 
(function() { 
    var send = XMLHttpRequest.prototype.send, 
    token = csrfToken; //csrfToken is global 
    XMLHttpRequest.prototype.send = function(data) { 
    this.setRequestHeader('X-CSRF-Token', token); 
    return send.apply(this, arguments); 
    }; 
}()); 

은 이제부터 모든 요청은 header에 CSRF 것입니다. 이것은 우리를위한 문제를 해결했습니다. 희망이 당신을도 도움이됩니다.

+0

Thx 그리고 당신이 짐작했던 것처럼 그것은 우리에게 적용되지 않습니다 – kiorky

2

nginx 디버그 로그에서 문제는 백엔드에서 응답을 조기에 반환했기 때문입니다. 마지막 sendfile() 호출에서 nginx는 12584911 바이트 중에서 2488810 만 보낼 수있었습니다.

... 
2014/12/03 01:57:23 [debug] 39583#0: *1 chain writer in: 00000000011CC5D0 
2014/12/03 01:57:23 [debug] 39583#0: *1 sendfile: @2776864 12584911 
2014/12/03 01:57:23 [debug] 39583#0: *1 sendfile: 2488810, @2776864 2488810:12584911 
2014/12/03 01:57:23 [debug] 39583#0: *1 chain writer out: 00000000011CC5D0 
2014/12/03 01:57:23 [debug] 39583#0: *1 event timer del: 35: 1417568303245 
2014/12/03 01:57:23 [debug] 39583#0: *1 event timer add: 35: 60000:1417568303254 
2014/12/03 01:57:23 [debug] 39583#0: *1 http upstream request: "/admin/edit_object/6?" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http upstream process header 
2014/12/03 01:57:23 [debug] 39583#0: *1 malloc: 00000000011CD000:262144 
2014/12/03 01:57:23 [debug] 39583#0: *1 recv: fd:35 369 of 262144 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy status 200 "200 OK" 
2014/12/03 01:57:23 [debug] 39583#0: *1 http proxy header: "X-Powered-By: Sails <sailsjs.org>" 
... 

그리고 백엔드는 200 OK 대답을 반환했습니다. 이 시점에서 nginx는 요청 본문의 나머지 부분을 보낼 이유가 없다고 생각하고 전송을 중단합니다. 이것이 불완전한 업로드 파일의 원인입니다. 또한 keepalive 업스트림 연결이 구성되어 있고 this bug이라고 표시되어 있기 때문에 관련없는 요청의 헤더가 표시됩니다.

테스트 코드에서와 같이 요청을 완전히 읽은 후에 만 ​​응답을 보내도록 백엔드 코드를 가르쳐서 문제를 해결해야합니다.

+0

그래, 전적으로 그 점에 동의하고 그 또한 게시물에 명확하지 않은 경우 그것은 내 결론되었습니다 :). 그것이 내가 지금 무서운 이유 중 하나입니다. – kiorky