I've been following this FormData tutorial here, however have yet to understand how the formData object works.
My input form
<input type="file" id="file-id" class="w300px rounded4px" name="file" placeholder="PDF file">
<button class="btn-upload-pdf" ng-click="asub.uploadPDF()">Upload</button>
Here is the upload button function:
this.uploadPDF = () => {
const formData = new FormData();
const fileInput = document.getElementById('file-id');
const file = fileInput.files[0];
formData.append('pdf-file', file);
console.log('formData', formData)
return ApiFactory.insightPDF(formData).then((res) => {
console.log('res', res);
return res;
});
};
When I log out the fileInput
object .files[0]
I see the file I just attached:
It would seem to mean that this object should be enough to send along to the POST. However this is the next step:
formData.append('pdf-file', file);
I log out formData
before I send it into my Factory and this is the result, I don't see the key pdf-file
or that PDF anywhere, just a bunch of methods? Where does the file get appended too? How does formData contain the actual PDF?
I need to attach something from the formData object I presume:
The Factory that makes the POST request
const insightPDF = (formData) => {
console.log(' formData', formData)
return $http.post('/app/api/insights_pdf', formData).then((res) => {
console.log('PDF uploaded res', res)
return res;
}).catch(apiError);
};
Set Content-Type: undefined
When posting objects created by the FormData API it is important to set the content type header to undefined
.
const insightPDF = (formData) => {
console.log(' formData', formData)
var config = { headers: {'Content-Type': undefined} };
return $http.post('/app/api/insights_pdf', formData, config)
.then((res) => {
console.log('PDF uploaded res', res)
return res;
}).catch(apiError);
};
Normally the AngularJS framework, automatically add the content type header as application/json
which overrides the content type set by the XHR Send() method. When the XHR API sends a FormData object, it automatically sets the content type to multipart/form-data
with the proper boundary.
From the Docs:
The $http service will automatically add certain HTTP headers to all requests
To explicitly remove a header automatically added via $httpProvider.defaults.headers
on a per request basis, Use the headers property, setting the desired header to undefined
. For example:
var req = {
method: 'POST',
url: 'http://example.com',
headers: {
'Content-Type': undefined
},
data: { test: 'test' }
}
— AngularJS $http Service API Reference - Setting HTTP Headers
FormData objects like blobs are host-defined objects non-native to JavaScript. Not all of their properties can be seen by console.log
or console.dir
. Files are a special type of blob. The data is not necessarily loaded from disk. Usually, the data is streamed from disk only when needed by a specific API.
Avoid Extra Overhead -- Send files directly
Content in multipart/form-data
uses base64 encoding which adds 33% extra overhead. If uploading only one file, it is more efficient to send the file blob directly.
//MORE Efficient; Avoids base64 encoding overhead
const insightPDF = (dataObject) => {
var config = { headers: {'Content-Type': undefined} };
return $http.post('/app/api/insights_pdf', dataObject, config)
.then((res) => {
console.log('PDF uploaded res', res)
return res;
}).catch(apiError);
};
var file = inputElem[0].files[0];
insightPDF(file);
If the server can accept binary content directly, it is best to send files that way. The XHR API will automatically set the content type to that of the file.