pqina / filepond

🌊 A flexible and fun JavaScript file upload library
https://pqina.nl/filepond
MIT License
15.25k stars 828 forks source link

Add option to send metadata as separate form data entries #212

Open juongithub opened 5 years ago

juongithub commented 5 years ago

Metadata is send as string but using the same field name that is used for the FILE input.

Currently the request payload looks like:

------WebKitFormBoundary
Content-Disposition: form-data; name="filepond"; filename="test.jpg"
Content-Type: application/octet-stream

[file data]
------WebKitFormBoundary
Content-Disposition: form-data; name="filepond"

{"field1":"1","field2":"2"}

note that "filepond" is used for both parameters.

Whereas it should be:

------WebKitFormBoundary
Content-Disposition: form-data; name="filepond"; filename="test.jpg"
Content-Type: application/octet-stream

[file data]
------WebKitFormBoundary
Content-Disposition: form-data; name="field1"

1
------WebKitFormBoundary
Content-Disposition: form-data; name="field2"

2

I can suggest to change the code in createProcessorFunction:

// create formdata object
var formData = new FormData();

// add metadata under same name
if (isObject(metadata)) {
    formData.append(name, JSON.stringify(metadata));
}

to

// create formdata object
var formData = new FormData();

// add metadata under same name
if (isObject(metadata)) {
    for (var k in metadata) {
        var v = metadata[k];
        formData.append(k, isObject(v) ? JSON.stringify(v) : v);
    }
}
rikschennink commented 5 years ago

Currently, the field name is the same so the server doesn't need to know which metadata is attached.

This is an interesting approach, I will give this some thought.

juongithub commented 5 years ago

Currently, the field name is the same so the server doesn't need to know which metadata is attached.

This is an interesting approach, I will give this some thought.

I think the current approach is good if you have to build a backend to adapt to FilePond, but less optimal if you want to use FilePond with a pre-existing backend (e.g., values to be passed to specific parameter names). Try to image if you do not have control on the API method you want to call. At least both possibilities should be offered.

rikschennink commented 5 years ago

@juongithub I'll mark this as an enhancement.

It's currently low priority as you can define a custom processing method. It should then be fairly easy to also send the metadata in a different way (using the code you posted above).

alessandromarchetti commented 4 years ago

When you say "you can define a custom processing method" how would you do it? I've looked for it in the docs but I couldn't figure it out.

rikschennink commented 4 years ago

Use this method: https://pqina.nl/filepond/docs/patterns/api/server/#process-1

FilePond.setOptions({
    server: {
        process:(fieldName, file, metadata, load, error, progress, abort, transfer, options) => {

            // fieldName is the name of the input field
            // file is the actual file object to send
            const formData = new FormData();
            formData.append(fieldName, file, file.name);

            const request = new XMLHttpRequest();
            request.open('POST', 'url-to-api');

            // Should call the progress method to update the progress to 100% before calling load
            // Setting computable to false switches the loading indicator to infinite mode
            request.upload.onprogress = (e) => {
                progress(e.lengthComputable, e.loaded, e.total);
            };

            // Should call the load method when done and pass the returned server file id
            // this server file id is then used later on when reverting or restoring a file
            // so your server knows which file to return without exposing that info to the client
            request.onload = function() {
                if (request.status >= 200 && request.status < 300) {
                    // the load method accepts either a string (id) or an object
                    load(request.responseText);
                }
                else {
                    // Can call the error method if something is wrong, should exit after
                    error('oh no');
                }
            };

            request.send(formData);

            // Should expose an abort method so the request can be cancelled
            return {
                abort: () => {
                    // This function is entered if the user has tapped the cancel button
                    request.abort();

                    // Let FilePond know the request has been cancelled
                    abort();
                }
            };
        }
    }
});
alessandromarchetti commented 4 years ago

I missed the advanced section of the server configuration about setting them as callables. Very nice feature! Thank you!

woodreamz commented 4 years ago

Hi,

If you implement options to configure how metadata are added, it could be great if we can have an option to remove the metadata part from the payload (at least when metadata is empty, currently, it sends an empty object stringified). On my side, I encounter the same issue as described here, my react app consumes many API and one is not supporting this "empty" part. For now, I will use your workaround and override the process but it could be great if we can configure it.

Thanks and good work for filepond!