How to correctly attach file to formData before POSTing to server?

I've been following this FormData tutorial here, however have yet to understand how the formData object works.

My input form

enter image description here

<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:

enter image description here

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?

enter image description here

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);
};

Answers:

Answer

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.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.