pqina / filepond

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

How to set formdata field name? #120

Closed nordbit closed 6 years ago

nordbit commented 6 years ago

My api consumes only data with this kind of structure: {member: {avatar: file}}

In dropzonejs I can set: paramName: 'avatar[file]' Is there an option similar to dropzonejs "paramName"?

rikschennink commented 6 years ago

I'm looking at the Dropzone docs, the paramName property basically sets the name attribute of the input field (which defaults to 'file').

If you want another name than file you can configure dropzone with the option paramName.

<input type="file" name="file" />

With FilePond you have the same option but the property is named name and is automatically copied from the file input element if one is used as a base.

So this will work:

<input type="file" name="avatar[file]" />

Or when using JavaScript options:

FilePond.create({
  name: 'avatar[file]'
})
nordbit commented 6 years ago

Hey, i'm slightly slow and can't get concept from docs.

For now my api got empty params form FilePond generated by:

const inputElement = document.querySelector('input[type="file"]');
const pond = FilePond.create( inputElement );

And effect after uploading, from server side: %{"file" => "{}"}

FilePond is wrapped only around input file field. How to implement around whole form with input file field and send all data form.

<form>
  <input type="text" name="username" />
  <input type="file" name="avatar" />
 <button type="submit">Submit</button>
</form>
rikschennink commented 6 years ago

FilePond only handles the file input field.

I think you have to alter the file input name to avatar[file].

<form>
  <input type="text" name="username" />
  <input type="file" name="avatar[file]" />
 <button type="submit">Submit</button>
</form>
nordbit commented 6 years ago

I found in chrome console:

------WebKitFormBoundary2pYKi1tW1oADnfT1
Content-Disposition: form-data; name="image[file]"; filename="recurly-header.jpg"
Content-Type: image/jpeg

------WebKitFormBoundary2pYKi1tW1oADnfT1
Content-Disposition: form-data; name="image[file]"

{}

Second request is rewriting first data, and I have no idea why filepond is sending file input field twice. I think this is a reason why my api endpoint can't get binary file.

%{"image" => %{"file" => "{}"}}

and after plug encode plugin:

------WebKitFormBoundaryAnHgIbnus7Bd9AOi
Content-Disposition: form-data; name="image[file]"; filename="recurly-header.jpg"
Content-Type: image/jpeg

------WebKitFormBoundaryAnHgIbnus7Bd9AOi
Content-Disposition: form-data; name="image[file]"

{"base64":"/9j/4Q1jRXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAExAAIAAAAiAAAAcgEyAAIAAAAUAAAAlIdpAAQAAAABAAAAqAAAANQACvyAAAAnEAAK/IAAACcQQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKFdpbmRvd3MpADIwMTg6MDE6MjQgMjA6NTA6MzUAAAOgAQADAAAAAf//AACgAgAEAAAAAQAAAyCgAwAEAAAAAQAAAYgAAAAAAAAABgEDAAMAAAABAAYAAAEaAAUAAAABAAABIgEbAAUAAAABAAABKgEoAAMAAAABAAIAAAIBAAQAAAABAAABMgICAAQ and more

and my api endpoint: %{"image" => %{"file" => "{\"base64\":\"/9j/4Q1jRXhpZgAATU0AKgAAAAgABwESAAMAAAABA and more

Why this file input field is sent twice? I am using simple html template with loading js/css, input filed and simple config. One instance of filepond initialized. Filepond version from repo lecheed today.

This is my whole HTML file:

<link rel="stylesheet" href="./vendor/filepond.css">
<link rel="stylesheet" href="./vendor/filepond-plugin-image-preview.css"/>

 <input type="file" name="image[file]" >

<script type="text/javascript" src="./vendor/filepond.js"></script>
<script type="text/javascript" src="./vendor/filepond-plugin-file-rename.min.js"></script>
<script type="text/javascript" src="./vendor/filepond-plugin-file-validate-size.min.js"></script>
<script type="text/javascript" src="./vendor/filepond-plugin-file-validate-type.min.js"></script>
<script type="text/javascript" src="./vendor/filepond-plugin-image-preview.min.js"></script>
<script>

const inputElement = document.querySelector('input[type="file"]');
FilePond.registerPlugin(
  FilePondPluginFileRename,
  FilePondPluginFileValidateType,
  FilePondPluginFileValidateSize,
  FilePondPluginImagePreview
);
const pond = FilePond.create( inputElement,   {
    labelIdle: `Drag & Drop your picture or <span class="filepond--label-action">Browse</span>`,
    imagePreviewHeight: 170
  });
FilePond.setOptions({
    server: {
      url: 'http://cordoba.net:4000',
      timeout: 7000,
      process: {
            url: '/media_manager/images',
            method: 'POST',
            withCredentials: false,
            headers: {
              "accept": "application/json;odata=verbose",
            },
            onload: function(response) {
                return response.key;
            },
            onerror: function(response) {
                return response.data;
            }
        },
        fetch: null,
        revert: null
}
});
</script>
rikschennink commented 6 years ago

It sends both the file object and the file item metadata. When using a PHP backend the file ends up in the $_FILES array and the metadata ends up in the $_POST array. Is this something you can configure with your backend API? I'm happy to look into it but I could not determine the server from the code snippet.

nordbit commented 6 years ago

Unfortunately I can not change my api code. It depends from other two modules, not maintained by me. They expect a specific data structure. In dropzonejs I can specify single file form upload and trigger POST with every single form/file individually. I'm closing for now, I see it is a api structure issue not filepond. Thank You for colabaration.

rikschennink commented 6 years ago

Alright, then I think you could opt for a custom processing method.

This is a quick outline of a custom processing method, haven't tested it thoroughly but this is basically it.

There's more info to be found here: https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects

And some information on the server config in the FilePond docs: https://pqina.nl/filepond/docs/patterns/api/server/#advanced

const pond = create(document.querySelector('input[type=file]'), {
    server: {
        process: (fieldName, file, metadata, load, error, progress, abort) => {

            // We ignore the metadata property and only send the file

            const formData = new FormData();
            formData.append(fieldName, file, file.name);

            const request = new XMLHttpRequest();
            request.open('POST', 'http://foo.com/submitform.php');

            request.upload.onprogress = (e) => {
                progress(e.lengthComputable, e.loaded, e.total);
            };

            request.onload = function() {
                if (request.status >= 200 && request.status < 300) {
                    load(request.responseText);
                }
                else {
                    error('oh no');
                }
            };

            request.send(formData);

        }
    }
});
nordbit commented 6 years ago

Yes, your solution works. Thank You for help.

wwarne commented 6 years ago

Thank you @rikschennink I am learning React and was trying to upload images to a backend powered by Ruby on Rails. The second part of request with file metadata ( with {} ) was rewriting the first one with actual data. And your solution just saved me! Thanks you a lot!

kodeine commented 5 years ago

Hello, form data field avatar[media] comes as empty in chrome inspector. Please let me know if i'm doing something wrong? Even though i can see the image in preview.

<input type="file" class="my-pond" />
$('.my-pond').filepond({
        name: 'avatar[media]',
        labelIdle: 'Drag & Drop your video or picture or <span class="filepond--label-action"> Browse </span>',
        // allowMultiple: false,
    });
screen shot 2018-12-07 at 10 25 50 pm
rikschennink commented 5 years ago

@kodeine I think you need to add the file encode plugin. Otherwise, FilePond won't post anything to the server as it's not possible to set values to a file input element.

kodeine commented 5 years ago

@rikschennink File encode plugin will make it to base64, the server I don't have control at and won't accept base64. Any other idea what should I do? I need to append formdata.

Is it possible to use something like

           let input = document.createElement('input');
            input.type = 'file';
            input.accept = "image/x-png,image/gif,image/jpeg";
            input.click();
            input.onchange = () => {
                let blob = window.URL.createObjectURL(input.files[0]);
                this.upload(blob, input.files[0]).catch(() => null);
            }
rikschennink commented 5 years ago

@kodeine You need to set either the server property, so FilePond can upload to the server asynchronously, or use the file encode plugin.

See File Encode plugin intro text for explanation: https://pqina.nl/filepond/docs/patterns/plugins/file-encode/

presiden commented 2 years ago

I'm looking at the Dropzone docs, the paramName property basically sets the name attribute of the input field (which defaults to 'file').

If you want another name than file you can configure dropzone with the option paramName.

<input type="file" name="file" />

With FilePond you have the same option but the property is named name and is automatically copied from the file input element if one is used as a base.

So this will work:

<input type="file" name="avatar[file]" />

Or when using JavaScript options:

FilePond.create({
  name: 'avatar[file]'
})

You save my life. Thank you