How to correctly attach file to formData before PO

2020-03-04 18:32发布

问题:

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

回答1:

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.