pqina / filepond

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

Custom Processing Method #95

Closed stack-its closed 6 years ago

stack-its commented 6 years ago

Ok, if I'm just trying to submit a file object to my backend using ajax. FYI - I'm running jquery plugin too. You suggested setting up a custom processing method. If I have the following code so far, I'm still shaky on a few things:

1) Seeing an error from 'FilePond:addfile' when browsing/dropping a file in: 202Not sure if this is because of preventDefault or just because I don't have everything hooked up yet?

2) I rewrote custom process as a variable assigned function. Not sure if I should change it to a regular named function and then call it?

3) How do I get a reference to the file that was browsed/dropped in?

4) Do I base64 encode the file & then pass it in as 'file' to the custom process function?

5) Do I need to pass in something for fieldName? Not sure what that is.

6) Is 'metadata' just simple array data that I can pass to my ajax along w/ the file, as I need to send a few things along w/ it?

7) What is totalSize in progress?

8) Is load needed if I'm just uploading a file?


FilePond.registerPlugin(
    FilePondPluginFileValidateSize,
    FilePondPluginImageResize,
    FilePondPluginImagePreview
);

$('.filepond').filepond({
  labelIdle: 'Drag & Drop File or <span class="filepond--label-action"> Browse </span>'
});

$('.filepond').on('FilePond:addfile', function(error, file) {
  if (error) {
    console.log('File Add Error: ' , error);
    return;
  }
  console.log('File Added', file.filename);  
});

var handler = function(fieldName, file, metadata, load, error, progress, abort) {
  // Custom ajax file upload or local storing here

  // Call the error method if something is wrong, should exit after
  error('Error occurred');
  // Call the progress method to update the progress to 100% before calling load
  // Setting computable to false switches the loading indicator to infinite mode
  // (computable, processedSize, totalSize)
  progress(true, 0, 1024);
  // Call the load method when done and pass the returned server file id
  // the load method accepts either a string (id) or an object
  // the unique server file id is used by revert and restore functions
  load('unique-file-id');
  // Abort method so the request can be cancelled by user
  return {
    abort: function() {
      // User tapped abort, cancel ongoing actions here
      // Let FilePond know the request has been cancelled
      abort();
    }
  };
};
rikschennink commented 6 years ago

Hi

  1. When listening for events you only receive the event object, so in this case you're logging the event object and not an error.

  2. A function or reference to function is fine.

  3. It is passed to your custom processing method as the second argument, you can also use the getFile method on your FilePond instance (see docs on how that would work with jQuery).

  4. No, FilePond will pass the file automatically you can then wrap it in a FormData object.

  5. FilePond will pass that automatically, it will be the name of the original file input element.

  6. FilePond will pass that automatically, it's the metadata attached to the file item.

  7. This is returned by the progress event, when using jQuery you'll probably have to add this manually to the $.ajax method.

  8. load is needed to return the id (or url or any kind of unique identifier) of the file on the server.

stack-its commented 6 years ago

Ok, Updated code, but still not getting things to work: Remember, you told me not to use FileEncode Plugin. InputNameTag is undefined & file.filename is undefined - not an object

I tried putting fpUpload inside of on('FilePond:addfile') w/ & w/o variables but still nothing, etc. It's my understanding that all these variables are auto-generated once a file is browsed/dropped. I need some code assistance to get these variables generated properly.


       FilePond.registerPlugin(
          FilePondPluginFileValidateSize,
          FilePondPluginImageResize,
          FilePondPluginImagePreview
        );

        $('.filepond').filepond({
          labelIdle: 'Drag & Drop File or <span class="filepond--label-action"> Browse </span>',
          dropValidation: true
        });

        function fpUpload(fieldName, file, metadata, load, error, progress, abort) {
          console.log('InsideFP-InputNameTag: ' + fieldName);
          console.log('InsideFP-Filename: ' + file.filename);
          console.log('InsideFP-Metadata: ' , metadata);
          // Custom ajax file upload or local storing here

          // Call the error method if something is wrong, should exit after
          error('Error occurred');
          // Call the progress method to update the progress to 100% before calling load
          // Setting computable to false switches the loading indicator to infinite mode
          // (computable, processedSize, totalSize)
          progress(true, 0, 1024);
          // Call the load method when done and pass the returned server file id
          // the load method accepts either a string (id) or an object
          // the unique server file id is used by revert and restore functions
          load('unique-file-id');
          // Abort method so the request can be cancelled by user
          return {
            abort: function() {
              // User tapped abort, cancel ongoing actions here
              // Let FilePond know the request has been cancelled
              abort();
            }
          };
        }

        fpUpload();

        $('.filepond').on('FilePond:addfile', function(e) {
          if (e.error) {
            console.log('File Add Error: ' , e.error);
            //return;
          }
        });
rikschennink commented 6 years ago

You need to set the fpUpload method to the server.process property. Best to read the documentation carefully.

$('.filepond').filepond({
    server:{
        process: fpUpload
    }
});
stack-its commented 6 years ago

Ok, I added that server setting. Wondering if this is the way to add additional key/value pairs to the process function?

$('.filepond').filepond({
          labelIdle: 'Drag & Drop File or <span class="filepond--label-action"> Browse </span>',
          dropValidation: true,
          server: {
            process: fpUpload,
            headers: {
              'key': value
            }  
          }  
});

I noticed that metadata is blank in console. When I look at array values added to my imgData = new FormData() I see 'File' (notice Uppercase). But when I look at log of imgData after, it's empty, I think.

204

Here's the code that's generating that:

function fpUpload(fieldName, file, metadata, load, error, progress, abort) {
    console.log('InsideFP-InputNameTag: ' + fieldName);
    console.log('InsideFP-Metadata: ' , metadata);
    var obj_data = {};
    //obj_data['something'] = 'something'; // may want to add other data
    obj_data['myfile'] = file;
    var imgData = new FormData();
    $.each(obj_data, function(k, v) { // apply object key/values to imgData
    console.log(['Key, Value & myfilecheck: ', k, v, k == 'myfile']);
        if (k == 'myfile') {$.each(v, function(i, file) {imgData.append(k, file)})}
    else {imgData.append(k, v)}
    });
    console.log('ImgData: ' , imgData);
    // $.ajax....
}

It seems file is not being added to imgData as expected, but not sure what I've missed. Almost there, just need to get file (and key/value data) to ajax :)

rikschennink commented 6 years ago

Hi,

No that's not the way to set the metadata. You can use the setMetadata method to add key/value pairs to file items. Each file can potentially have different metadata.

Note that console.log on FormData objects will log an empty FormData object, the data will however be there

stack-its commented 6 years ago

Ok, I removed the headers stuff. I was wondering if I could just add key/value pairs to my obj_data, sim. to the line I commented out above in function fpUpload ?

Also, I realized I didn't have my inputs wrapped in a form tag w/ enctype="multipart/form-data" method="post", so did that. But now the Browse text doesn't work. I did have an unbind called on my wrapping span, but not working now. Tried unbind on the form, but still doesn't work. Have you run into this problem?

rikschennink commented 6 years ago

You can, you don't have to use the metadata value, it's there for your convenience, you can do whatever you want in the processing method.

The form won't be multipart as uploads are done async. Sounds like a tag that's not closed properly.

stack-its commented 6 years ago

Sigh... I don't know what's wrong w/ the CODE ! I need help w/ the code.

Typical html generated before browse/drop for file:

<span id="something_rowset_dataset1_1~~field9" class="something" data-id="5" data-img1fld="field9" data-i2xsmfld="" data-i2xlgfld="" data-alttxtfld="field12" data-upload="../resources/testupload/" style="opacity: 1; visibility: visible;">
  <span class="something">
    <form method="post" id="something_form_dataset1_1~~field9">
      <label class="img1">field9:</label><i class="fa fa-pencil fa-flip-horizontal" aria-hidden="true"></i><input class="img1" type="text">
      <div class="filepond--root filepond filepond--hopper" style="height:82px">
        <input class="filepond--browser" type="file" id="filepond--browser-pvy9wqusy" aria-controls="filepond--assistant-pvy9wqusy" aria-labelledby="filepond--drop-label-pvy9wqusy">
        <div class="filepond--drip"></div>
        <div class="filepond--drop-label" style="transform:translate3d(0px, 16px, 0);opacity:1"><label for="filepond--browser-pvy9wqusy" id="filepond--drop-label-pvy9wqusy" aria-hidden="true">Drag &amp; Drop your files or <span class="filepond--label-action" tabindex="0">Browse</span></label>  </div>
        <div class="filepond--list-scroller" style="transform:translate3d(0px, 16px, 0)"><ul class="filepond--list" role="list"></ul></div>
        <div class="filepond--panel filepond--panel-root" data-scalable="true">
          <div class="filepond--panel-top filepond--panel-root"></div><div class="filepond--panel-center filepond--panel-root" style="transform:translate3d(0px, 8px, 0) scale3d(1, 0.66, 1)"></div>
          <div class="filepond--panel-bottom filepond--panel-root" style="transform:translate3d(0px, 74px, 0)"></div>
        </div>
        <span class="filepond--assistant" id="filepond--assistant-pvy9wqusy" role="status" aria-live="polite" aria-relevant="additions"></span>
      </div>
      <label class="alttxt">field12:</label><i class="fa fa-pencil fa-flip-horizontal" aria-hidden="true></i><input class="alttxt" type="text">
    </form>
  </span>
</span>

I discovered clicking on Browse wasn't working because of some styling. I changed it to this to move it up & get it above other elements, so at least the browse window shows up now.

.filepond--browser {
  margin-top: -2em;
  cursor: pointer;
  z-index: 20}

JS is now this (File never reaches fpUpload function, i.e. it never runs) & Preview image never shows):


function mainFileUpload(upload, formid) {
        FilePond.registerPlugin(
          FilePondPluginFileValidateSize,
          FilePondPluginImageResize,
          FilePondPluginImagePreview
        );
        console.log('Upload in edbFileUpload: ' + upload); // string is ok
        console.log('Formid in edbFileUpload: ' + formid);  // string is ok
        $('.filepond').filepond({
          labelIdle: 'Drag & Drop File or <span class="filepond--label-action"> Browse </span>',
          dropValidation: true,
          server: {
            process: fpUpload
          }  
        });
        $('.ffproedit').find('br').remove();
        $('.filepond').on('FilePond:addfile', function(e) {
          console.log('file added event', e);   // data inside event is Undefined
        });
        function fpUpload(fieldName, file, metadata, load, error, progress, abort) {
          console.log('InsideFP-InputNameTag: ' + fieldName);  // Undefined
          console.log('InsideFP-MetadataBefore: ' , metadata);  // Undefined
          console.log('InsideFP-Upload: ' , upload);
          console.log('InsideFP-FormID: ' , formid);
          console.log('InsideFP-File: ' , file);  // Undefined
          var obj_data = {};
          obj_data['upload'] = upload;
          obj_data['myfile'] = file;
          var imgData = new FormData($('form#' + formid)[0]);
          $.each(obj_data, function(k, v) { // apply object key/values to imgData
            console.log(['Key, Value & myfilecheck: ', k, v, k == 'myfile']); // v.constructor === FileList
            if (k == 'myfile') {$.each(v, function(i, file) {imgData.append(k, file)})}  // v.constructor === FileList
            else {imgData.append(k, v)}
      });
      $.ajax({
            url: 'path/uploadimg.php',
            type: 'POST',
            data: imgData,
            dataType : 'json',
            contentType: false,
            processData: false
          })
          ...
       }
}
rikschennink commented 6 years ago

I've created a Codepen that works, maybe you can use it as a starting point: https://codepen.io/rikschennink/pen/XBwpNM?editors=1011

stack-its commented 6 years ago

You previously said I didn't need encoding in my custom process function & no need to use FilePond's encode plugin.

When I check in my php file, & do a var_dump of $_FILES - it's empty! and the file and the other variables are in $_POST. So for example, $filename = $_FILES["myfile"]["name"]; is throwing an error that name is undefined.

Are my ajax settings above correct ? Is there some other code that I am missing in js to create myfile in $_FILES ?

rikschennink commented 6 years ago

As you’re using a custom processing method that depends on your implementation, if you create a formdata object and append the file to it in the correct way (there’s some tutorials on this online) and post using jQuery Ajax then it should be sent as a file object and end up in the FILES array.

stack-its commented 6 years ago

I think the file in fpUpload function is not a standard js file object & thus can't be uploaded as a file object using normal methods. I've tried using this code in the function to get the normal file object dropped/browsed to, but still no success - maybe if you provide some code to interface w/ your plugin.


$(formid + ' input[type="hidden"]').filepond('getFile').then(function(nfile) {
              console.log('Got file', nfile);  // file to use in FormData
});

'formid' is of course the form's id that wraps the Filepond input.
rikschennink commented 6 years ago

The custom processing method receives a plain file object. The getFile method returns a file item, the file property of the file item is a plain file object.