2016-08-26 4 views
2

LAGE:org.springframework.web.multipart.MultipartException: konnte nicht parsen mehrteiliger Anfrage Servlet ... Stream beendet unerwartet

eine mehrteilige Form Anforderung von Node.js Einreichen (über den Knoten Kern HTTPS-Modul) mit einer Feder -boot Java-API. Die API erfordert zwei Formdatenelemente:

"route"
"files"

FULL ERROR: Exception processed - Main Exception: org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is org.apache.commons.fileupload.FileUploadException: Stream ended unexpectedly

REQUEST HEADERS:

{"Accept":"*/*", 
    "cache-control":"no-cache", 
    "Content-Type":"multipart/form-data; boundary=2baac014-7974-49dd-ae87-7ce56c36c9e7", 
    "Content-Length":7621} 

FORM-Daten, die geschrieben werden (alle als binär geschrieben):

Content-Type: multipart/form-data; boundary=2baac014-7974-49dd-ae87-7ce56c36c9e7 

--2baac014-7974-49dd-ae87-7ce56c36c9e7 

Content-Disposition:form-data; name="route" 

...our route object 

--2baac014-7974-49dd-ae87-7ce56c36c9e7 
Content-Disposition:form-data; name="files"; filename="somefile.xlsx" 
Content-Type:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 

...excel file contents 

--2baac014-7974-49dd-ae87-7ce56c36c9e7-- 

NODE Code:

let mdtHttpMultipart = (options, data = reqParam('data'), cb) => { 
    const boundaryUuid = getUuid() 
    , baseHeaders = { 
     'Accept': '*/*', 
     'cache-control': 'no-cache' 
     } 
    , composedHeaders = Object.assign({}, baseHeaders, options.headers) 
    ; 

    options.path = checkPath(options.path); 

    let composedOptions = Object.assign({}, { 
    'host': getEdiHost(), 
    'path': buildPathFromObject(options.path, options.urlParams), 
    'method': options.method || 'GET', 
    'headers': composedHeaders, 
    'rejectUnauthorized': false 
    }); 


    composedOptions.headers['Content-Type'] = `multipart/form-data; boundary=${boundaryUuid}`; 

    let multipartChunks = []; 
    let dumbTotal = 0; 

    let writePart = (_, encType = 'binary', skip = false) => { 
    if (!_) { return; } 

    let buf = Buffer.from(_, encType); 
    if (!skip) {dumbTotal += Buffer.byteLength(buf, encType);} 
    multipartChunks.push(buf); 
    }; 

    writePart(`Content-Type: multipart/form-data; boundary=${boundaryUuid}\r\n\r\n`, 'binary', true) 
    writePart(`--${boundaryUuid}\r\n`) 
    writePart(`Content-Disposition:form-data; name="route"\r\n`) 
    writePart(JSON.stringify(data[0]) + '\r\n') 
    writePart(`--${boundaryUuid}\r\n`) 
    writePart(`Content-Disposition:form-data; name="files"; filename="${data[1].name}"\r\n`) 
    writePart(`Content-Type:${data[1].contentType}\r\n`) 
    writePart(data[1].contents + '\r\n') 
    writePart(`\r\n--${boundaryUuid}--\r\n`); 

    let multipartBuffer = Buffer.concat(multipartChunks); 

    composedOptions.headers['Content-Length'] = dumbTotal; 
    let request = https.request(composedOptions); 

    // on nextTick write multipart to request 
    process.nextTick(() => { 
    request.write(multipartBuffer, 'binary'); 
    request.end(); 
    }); 

    // handle response 
    request.on('response', (httpRequestResponse) => { 
    let chunks = [] 
     , errObject = handleHttpStatusCodes(httpRequestResponse); 
    ; 

    if (errObject !== null) { 
     return cb(errObject, null); 
    } 

    httpRequestResponse.on('data', (chunk) => { chunks.push(chunk); }); 
    httpRequestResponse.on('end',() => { 
     let responseString = Buffer.concat(chunks).toString() 
     ; 

     return cb(null, JSON.parse(responseString)); 
    }); 

    }); 

    request.on('error', (err) => cb(err)); 
}; 

Wir finden keinen Grund für die 500 sehen können, basierend auf der Spezifikation geworfen werden. Tonnenweise herumprobieren mit dem Format hier, aber wir haben das Ergebnis noch nicht richtig erreicht.

SEITENHINWEIS: Es funktioniert für uns mit POSTMAN, kann es einfach nicht mit unserem eigenen Anwendungsserver (wo wir die Excel-Datei erstellen) arbeiten.

Jede Hilfe würde geschätzt, auch wenn nur Ideen zu versuchen.

+0

Wahrscheinlich nicht das Problem, aber das erste Mal, wenn Sie rufen 'writePart()' Sie hinzufügen den Content-Type-Header die ist bereits für Sie hinzugefügt, wenn Sie 'composedOptions.headers ['Content-Type'] = ...' angeben. Sie brauchen auch keine Zeilenumbrüche um das letzte '- $ {boundaryUuid} -'. – idbehold

+0

ging voran und machte diese Änderungen, immer noch das gleiche Ergebnis wie erwartet. –

+0

Sind Sie sicher, dass die binäre Kodierung? Das ist ein Alias ​​für 'latin1'. Sehen Sie, ob 'base64'-Kodierung alles einen Unterschied macht? –

Antwort

2

Versuchen Sie folgendes:

let mdtHttpMultipart = (options, data = reqParam('data'), cb) => { 
    const boundaryUuid = getUuid() 
    , baseHeaders = { 
     'Accept': '*/*', 
     'cache-control': 'no-cache' 
     } 
    , composedHeaders = Object.assign({}, baseHeaders, options.headers) 
    ; 

    let file = data[1] 
    let xlsx = file.contents 

    options.path = checkPath(options.path); 

    let composedOptions = Object.assign({}, { 
    'host': getEdiHost(), 
    'path': buildPathFromObject(options.path, options.urlParams), 
    'method': options.method || 'GET', 
    'headers': composedHeaders, 
    'rejectUnauthorized': false 
    }); 

    let header = Buffer.from(`--${boundaryUuid} 
    Content-Disposition: form-data; name="route" 

    ${JSON.stringify(data[0])}) 
    --${boundaryUuid} 
    Content-Disposition: form-data; name="files"; filename="${file.name}" 
    Content-Type: ${file.contentType} 

    `.replace(/\r?\n */gm, '\r\n')) 
    let footer = Buffer.from(`\r\n--${boundaryUuid}--`) 
    let length = header.length + xlsx.length + footer.length 
    let body = Buffer.concat([header, xlsx, footer], length) 

    composedOptions.headers['Content-Length'] = length; 
    composedOptions.headers['Content-Type'] = `multipart/form-data; boundary=${boundaryUuid}`; 

    let request = https.request(composedOptions); 

    // handle response 
    request.on('response', (httpRequestResponse) => { 
    let chunks = [] 
     , errObject = handleHttpStatusCodes(httpRequestResponse); 
    ; 

    if (errObject !== null) { 
     return cb(errObject, null); 
    } 

    httpRequestResponse.on('data', (chunk) => { chunks.push(chunk); }); 
    httpRequestResponse.on('end',() => { 
     let responseString = Buffer.concat(chunks).toString() 
     ; 

     return cb(null, JSON.parse(responseString)); 
    }); 

    }); 

    request.on('error', (err) => cb(err)); 

    // write multipart to request 
    request.end(body); 
}; 
+0

Mein Mann! Das hat es getan. Vielen Dank! –

0

Ist es, dass Sie nicht request.end() überall anrufen?

Das (sehr allgemeine) Formular zum Senden einer Anfrage mit einem Körper ist https.request(opts).end(body).

Auch könnten Sie nur request.write(buf) rufen jedes Mal, wenn Daten wollen anstatt schicken der Akkumulation in einen riesigen Puffer (und Sie sollten es nicht auf nextTick tun müssen) (EDIT: als OP Punkte in den Kommentaren geht diese verhindern Content-Length Einstellung, so vielleicht halten wie)

+0

Ich habe die Anfrage hinzugefügt .end() nachdem wir den buf geschrieben haben. Wir hatten früher diesen Code, der jeden Teil einzeln schrieb, wir haben ihn einfach geändert, um zu versuchen, die Länge des Inhalts zu berechnen, bevor wir die Anfrage gesendet haben, da wir dachten, dass dies das Problem sein könnte, aber das scheint es nicht zu sein. Immer noch der gleiche Fehler nach dem Hinzufügen von request.end(). Ich werde mein Codebeispiel oben ändern. Danke aber .. –

Verwandte Themen