-
-
Notifications
You must be signed in to change notification settings - Fork 11.2k
Description
Section/Content To Improve
Using multipart/form-data (to upload files and raw data.)
Suggested Improvement
Add the section
Relevant File(s):
README.md
Upload multipart/form-data
let FormData = require( 'form-data' );
The form-data
package has some extra features compared to the native formData class, which we'll use
Create a new FormData object, add content.
// Random 8-bit (uint) data.
let dataInBuffer = new Buffer.from( [0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0xFF] );
let formData = new FormData();
formData.append( 'id', 1 );
formData.append( 'string', 'Text we want to add to the submit' );
formData.append( 'file', dataInBuffer, {
filename: 'someFileName.bin',
contentType: 'application/octet-stream',
} );
Axios does not turn the formData object in a proper Buffer/stream. We need to do this manually.
Turn the formData object into a Buffer, so Axios can handle it.
let formDataToBufferObject = formDataToBuffer( formData );
A proper multipart/form-data request has a header for each parameter. Each header consists of --boundary
.
The form-data
package automatically creates these headers, including all breaks \r\n
needed. We do however
need to add a break after our data. Below code will handle it for us.
function formDataToBuffer( formData ) {
let dataBuffer = new Buffer( 0 );
let boundary = formData.getBoundary();
for( let i = 0, len = formData._streams.length; i < len; i++ ) {
if( typeof formData._streams[i] !== 'function' ) {
dataBuffer = this.bufferWrite( dataBuffer, formData._streams[i] );
// The item have 2 more "-" in the boundary. No clue why
// rfc7578 specifies (4.1): "The boundary is supplied as a "boundary"
// parameter to the multipart/form-data type. As noted in Section 5.1
// of [RFC2046], the boundary delimiter MUST NOT appear inside any of
// the encapsulated parts, and it is often necessary to enclose the
// "boundary" parameter values in quotes in the Content-Type header
// field."
// This means, that we can use the boundary as unique value, indicating that
// we do NOT need to add a break (\r\n). These are added by data-form package.
//
// As seen in this example (https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST#Example)
// the boundary is preceded by 2x "-". If thus --Boundary exists, do not add the break.
if( typeof formData._streams[i] !== 'string' || formData._streams[i].substring( 2, boundary.length + 2 ) !== boundary ) {
dataBuffer = this.bufferWrite( dataBuffer, "\r\n" );
}
}
}
// Close the request
dataBuffer = this.bufferWrite( dataBuffer, '--' + boundary + '--' );
return dataBuffer;
}
// Below function appends the data to the Buffer.
function bufferWrite( buffer, data ) {
let addBuffer;
if( typeof data === 'string' ) {
addBuffer = Buffer.from( data );
}
else if( typeof data === 'object' && Buffer.isBuffer( data ) ) {
addBuffer = data;
}
return Buffer.concat( [buffer, addBuffer] );
}
Finally, provide the buffer to Axios. Make sure to set the header as well!
axios.post( 'https://example.com/path/to/api',
formDataToBufferObject,
formData.getHeaders()
)
...
or if you want to specify your own headers
axios.post( 'https://example.com/path/to/api',
formDataToBufferObject,
{
headers: {
User_Agent: 'Some Useragent',
Accept: 'application/json',
'Content-Type': formData.getHeaders()['content-type'],
},
} )
...