pqina / filepond

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

Uploading File manually #24

Closed Quadriphobs1 closed 6 years ago

Quadriphobs1 commented 6 years ago

Is it possible to upload the file manually by letting the user click on a normal submit button alongside with a form either using ajax or direct post

rikschennink commented 6 years ago

@Quadriphobs1 It's not possible to upload with direct post ( as you cannot assign files to a file input, this is where the File Encode plugin is useful ). You can call the processFiles method to start uploading files manually. Use the getFile method to retreive specific file items. You can then send those to the server manually.

edit in 2023: You can now set the file input value in all major browsers, to do this with FilePond, set storeAsFile to true

This means that if you have a form with a file input and server side logic that handles the file(s) upload, you can add FilePond to you file input and you won't have to make changes on the server. No need for the file encode plugin.

Quadriphobs1 commented 6 years ago

Okay, I seems to get that part but where am little confused is the processFiles method which requires query, what is the query structured required here like... I do hope you can help with that

rikschennink commented 6 years ago

@Quadriphobs1 Ah, tested, and indeed it didn't work when omitting arguments. Just released a fix version 1.2.9. You can now trigger uploading of all files by calling myPond.processFiles() without arguments.

Quadriphobs1 commented 6 years ago

Although I don't seem to get what you meant but what I am trying now is I created server in the setOptions which the process goes to a route but then I can't trace an error of why the upload is not working and there is no way to actually check for error with the option, or maybe I don't about it yet, maybe you can put me through that :)

rikschennink commented 6 years ago

If I understand correctly you want:

  1. User clicks button outside of FilePond, this starts the upload of all selected files
  2. A custom upload method is used to upload the files

Is that correct?

If so, can you describe which step is difficult and why?

DamSenViet commented 6 years ago

I need to submit a form and other fields along with the files in an AJAX post. Would it be possible to attach and add the base64 encoded data to my request?

What I'm aiming for is to submit both the file (base64 encoded) and other fields I need. e.g. I want this object uploaded

{
    username: '*****',
    password: '*****',
    imgdata: '<base64 encoded file as a string>' // or Object that filePond is attempting to send
}

If I'm making the right assumptions I would have to read in each file inside the instance manually. I would base64 encode via the provided FileReader and attach the string to my post request, but that would mean filePond basically just acts as GUI and has no functional purpose. Is it possible to get the Object that filePond encode will send off?

I would still like to see the upload progress being updated on the filePond instance when I manually submit the form. Would this be possible?

rikschennink commented 6 years ago

@DamSenViet You can use the File Encode plugin to have FilePond encode the files as base64 data. You can then send it along with a normal form post ( not using AJAX ).

If you want to send data asynchronously you can use a FormData object to store all the values and files, this is what FilePond uses to send data to the server.

To add data to files you can use the setMetadata method on the file object, this data will be sent along with the file.

If you need to customize the request further you can set up a custom processing method, it provides hooks to control the FilePond interface ( progress indicators and errors/success state ).

Quadriphobs1 commented 6 years ago

So finally I got to upload a file and in my controller I return the generated ID for the file but how would I add the return key to an hidden input and since I would prefer using the file-encode-plugin, how can someone attach the file-encode to an input filed... am just lost a little bit on it

rikschennink commented 6 years ago

If you're using the File Encode plugin you don't need a process entry in the server configuration as you'll be uploading files on form submit ( note that this will cause trouble for large files ). When using the File Encode plugin the hidden inputs FilePond generates contain the file data.

nifte commented 6 years ago

@rikschennink Could you provide an example of how to add the encoded files to a FormData object? I'm having trouble figuring it out.

DamSenViet commented 6 years ago

@nifte check out the add files before encode. The op dropped a codepen that checks on the element with the img data.

nifte commented 6 years ago

@DamSenViet I'm not seeing that codepen anywhere... 😅 Also, I believe I'm trying to do exactly what you were trying to do. Did you figure out a way to add the files along with other form inputs to your FormData for an AJAX-based submit?

DamSenViet commented 6 years ago

Oh my bad, it was an issue and I was typing on my phone.

Here ya go:

https://github.com/pqina/filepond/issues/28

The issue's been fixed, but the codepen in the thread should show you how to extract the data.

nifte commented 6 years ago

@DamSenViet So, I figured out how to access the Base64-encoded data for each file, but I'm still confused on how to actually attach them to a FormData object like @rikschennink mentioned above...

DamSenViet commented 6 years ago

@nifte C'mon man we're all developers but you gotta learn how to use the APIs.

https://developer.mozilla.org/en-US/docs/Web/API/FormData/append

Really not that hard to Google "formData".

nifte commented 6 years ago

@DamSenViet I know how to use the FormData object. The part I'm confused on is what to actually append to the FormData (i.e. the 'value' parameter). What am I supposed to do with the JSON that Filepond gives me?

DamSenViet commented 6 years ago

@nifte well the file is base64 encoded. Get the base64 string via the data json and attach that to your FormData. Send that off then process the data server side (save/write in base64 mode, etc).

nifte commented 6 years ago

@DamSenViet Okay, this is what I've got:

var form = new FormData();
for (let i = 0; i < $('.filepond--list input').length; i++) {
    let file = JSON.parse($('.filepond--list input').eq(i).val());
    form.append('file' + i, file.data);
}

I'm taking the data from each hidden input, and appending it to the FormData object. But when I check the $_POST object on my server, it just shows each file with the data as a string.

DamSenViet commented 6 years ago

That's exactly what a file is. It's literally just a giant string, usually in binary as 0's & 1's. If you open a file in binary reading mode that's literally all you will see. When you use base64 encoding it helps compress the binary representation.

e.g. Here's why we base64 15 in binary (base 2) is "1111" = 4 characters = 4 bytes 15 in hex (base 16) is "F" = 1 character = 1 byte Imagine you have an img in binary that's millions of characters long. No server wants to handle that kind of load. We need to take the burden off the network since it can create bottlenecks.

You can either send a string that's either 4 million bytes you can send a string that's 1 million bytes. It's pretty obvious what you should be picking. As the file size gets bigger, the benefits get better (relatively). If you're wondering why we can't send it in binary directly, there's really nothing stopping you if you know how to manipulate AJAX dataType. It's just the browser does not let you just grab the file data on your own. It's prohibited as a security measure. That's why we use the FileReader object to get the base64 representation, because it's an open API (and currently the only method available to us).

That's also why we send the string off in base64 encoding. You take the string and write to a new file in base64 mode (turns base64 back into binary). If you can't write in base64 mode, decode the base64 string and write the resulting decoded string.

In whatever language you're using you need to decode base64 the file. Base64 is standard so I doubt you'll have trouble finding a library.

e.g. in a node.js server you would do something like this if you wanted to save the file somewhere:

var fs = require("fs");
fs.writeFile("out.png", base64String, 'base64', function(err) {
  console.log(err);
}

That would be the entire upload operation

If this sounds foreign to you, I can't help you.

rikschennink commented 6 years ago

@nifte If you're going to send FormData objects, why not use a custom process method instead of the File Encode plugin?

nifte commented 6 years ago

@DamSenViet Thank you for the breakdown. I've never messed around with Base64, so I'll do some more research. I do understand the concept, though, from your explanation. I'm using PHP/Apache, as opposed to NodeJS, but I'm sure it works similarly.

@rikschennink I already have a php file that handles files submitted from my form. I'm basically just trying to use Filepond as the UI for the file inputs in my form, so I'll use whatever the easiest solution is.

rikschennink commented 6 years ago

@nifte If you’re submitting async set the server property to your PHP file location. If you’re submitting a form ( page reloads ) then FormData is not going to help you (as it’s for use with fetch or XMLHttpRequest), you’ll need to adjust the backend code to accept encoded files ( see the PHP boilerplate for the required code changes ).

GeSeRnnov commented 5 years ago

@rikschennink Hello. Could you help with my task, it similar to the tasks above. I need to submit form fields data (name, email...) + loaded files to the server. Can I use processFiles method in the handle submit function (when the user filled all fields and loaded files into server/api/tmp by \ and pressed "Submit") and how to call it in React? I mean I don't have a link to Filepond:

import { FilePond, File, registerPlugin } from 'react-filepond';

handleSubmit(event){
    event.preventDefault();

    const pond =  ????
    pond.processFiles().then(files => {
           // files have been processed
    });

    const data = new FormData();
    data.append('name', this.state.name)        
    data.append('files', this.state.files)
    ...
    data.append( ... )

     axios
        .post('/api/send.php', data, ...)
        .then(...)
        ...
}

<form>
    <TextField.../>
    ...
    <TextField.../>
    <Filepond ref={ref => this.pond = ref}
        allowMultiple={true}                       
        server="api/"
        oninit={() => this.handleInit() }                   
        onupdatefiles={(fileItems) => {
            this.setState({
              files: fileItems.map(fileItem => fileItem.file)
            });
    }}>
        {this.state.files.map(file => (
        <File key={file} src={file} origin="local" />
        ))}
    </Filepond>
    <button type="submit" >Submit</button>
</form>
rikschennink commented 5 years ago

@GeSeRnnov You have a reference to FilePond, you set it here:

<Filepond ref={ref => this.pond = ref}>

this.pond will point to the FilePond instance.

I advise to let FilePond send the files to the server, then when you submit the form with Axios you only send the file IDs returned by the server. Now the server knows which files have been uploaded and can move the files to the right location/database.

If that's not what you want, then use this.pond.getFiles() and add the file objects (fileItem.file) to the data before sending everything in one go to the server with Axios.

GeSeRnnov commented 5 years ago

@rikschennink, your advise was helpful for me. All works now)). Thank you very much.

hooziewhatsit commented 5 years ago

I advise to let FilePond send the files to the server, then when you submit the form with Axios you only send the file IDs returned by the server. Now the server knows which files have been uploaded and can move the files to the right location/database.

How exactly do to we get the list of IDs back from filepond? I have the load(id); saving them as part of the process: function, but I haven't been able to find any documentation on getting that list back out? Technically I can make my own array with the unique IDs, but using filepond seems cleaner...

I'm uploading multiple images to the server and saving them, then when they finish adding text on their post they hit 'Save'. At that point I want to send the post information, as well as the list of IDs corresponding to that post so the server can then do some stuff.

rikschennink commented 5 years ago

getFiles

hungdev commented 5 years ago

@rikschennink thanks you.

  async onUpload() {
    const { arrFiles } = this.state
    console.log('arrFiles', arrFiles)
    var data = new FormData();
    _.forEach(arrFiles, file => {
      data.append('ebookFile', file.file);
    })
    // data.append('hi', 'hung')
    try {
      const response = await uploadFiles(data)
      console.log('response', response)
    } catch (e) {
      console.log(e)
    }
  }

for those who come later.

surfer77 commented 5 years ago

This was helpful for me for future people with this specific problem: https://github.com/pqina/filepond-docs/blob/master/content/patterns/API/server.md

siddmegadeth commented 5 years ago

How to work using this kind of Data . I am getting this result as an Array . There could be 10 images. I am using Cordova and there are no input here. all is done using a native media picker used in my Cordova app and post selection of images from this gallery picker i get the result as follow. No i want to use filepond to upload this data to my server which is written in NODEJS

> 
> {
> exifRotate: 0
> index: 0
> mediaType: "image"
> name: "Screenshot_20190702-023602.png"
> path: "/storage/emulated/0/Pictures/Screenshots/Screenshot_20190702-023602.png"
> quality: 100
> size: 1033217
> src: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD"
> thumbnailBase64: "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDABALDA4MChAODQ4SER"
> uri: "file:///storage/emulated/0/Pictures/Screenshots/Screenshot_20190702-023602.png"
> __proto__: Object
> }
> 
> 
>  

Any Suggestions

rikschennink commented 5 years ago

@siddmegadeth In theory you should be able to add the path and FilePond will try to load the file from that location. You can do this with the addFile method.

If that doesn't work you can load the file object with FileReader and then pass it to FilePond.

siddmegadeth commented 5 years ago

If possible can you provide exmaple. I am having a hardtime going through filepond documentation. All API is listed but just don't know from where to start and how to use, specific to my usecase. Mostly cover input type="file" .

rikschennink commented 5 years ago

@siddmegadeth If you have a filePond instance with a server location.

const pond = FilePond.create({ server:'./api' });
pond.addFile('./path/to/file');
siddmegadeth commented 5 years ago

How can i send Profile ID or any user identification . I am not able to send user unique ID so that i can query this user and create new file or else append this file if user is found. Right now i cannot send information which i can extract in req.body

Plus how do i trigger upload for above options

I am using Cordova-Native-Photo and this the format i receive https://github.com/DmcSDK/cordova-plugin-mediaPicker

{index: 0
mediaType: "image"
name: "IMG_20190711_184619078.jpg"
path: "/storage/emulated/0/DCIM/Camera/IMG_20190711_184619078.jpg"
size: 5167613
uri: "file:///storage/emulated/0/DCIM/Camera/IMG_201907}

I am not able to add this to filePond using pond.addFile('./path/to/file'); Please suggest

rikschennink commented 5 years ago

@siddmegadeth please post questions to StackOverflow as described in the issue template. This is related to Cordova not FilePond.

zyyoo commented 4 years ago

Is that easy to understand? FormData + getFiles

 html:
<input type="file" class="filepond" name="filepond" id="id_filepond" multiple data-max-file-size="3MB" data-max-files="3" /><button id ='test'>3232</button>
js:
           const pond = FilePond.create(document.querySelector('#id_filepond'), {
                labelIdle: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
            });
            $('#test').click(function () {
                const files = pond.getFiles()
                console.log(files)
                const params = new FormData();
                params.append('test', 'hahaha'); // Your custom data
                for ( var i in files) {
                    params.append('file', files[i].file);
                }
                $.ajax({
                    url: "xxxxxxxx",
                    type: "POST",
                    processData: false,
                    contentType: false,
                    data: params,
                    success: function (d) {
                    }
                });
            })
JonathanSchndr commented 3 years ago

This is my solution, inspired by @zyyoo

HTML
<input type="file" id="data" name="data" multiple>

Create FilePond
let filesObj = FilePond.create(document.getElementById("data"), {});

Collect Form
let formData = new FormData($("XX")[0]);
formData.delete("data"); //data is the name of the orignal input file; import to delete
  $.each(filesObj.getFiles(), function (i, file) { //filesObj is the instance of FilePond.create()
  formData.append('file-' + i, file.file);
});

Submit Form
$.ajax({
  url: "XX",
  method: "POST",
  data: formData,
  cache: false,
  processData: false,
  contentType: false
}).done((data) => {});
PPHelios commented 1 year ago

@GeSeRnnov You have a reference to FilePond, you set it here:

<Filepond ref={ref => this.pond = ref}>

this.pond will point to the FilePond instance.

I advise to let FilePond send the files to the server, then when you submit the form with Axios you only send the file IDs returned by the server. Now the server knows which files have been uploaded and can move the files to the right location/database.

If that's not what you want, then use this.pond.getFiles() and add the file objects (fileItem.file) to the data before sending everything in one go to the server with Axios.

@GeSeRnnov You have a reference to FilePond, you set it here:

<Filepond ref={ref => this.pond = ref}>

this.pond will point to the FilePond instance.

I advise to let FilePond send the files to the server, then when you submit the form with Axios you only send the file IDs returned by the server. Now the server knows which files have been uploaded and can move the files to the right location/database.

If that's not what you want, then use this.pond.getFiles() and add the file objects (fileItem.file) to the data before sending everything in one go to the server with Axios.

thanks for this great project and sorry for reopening this issue, I'm trying to upload files manually and managed to make it work, but the uploaded files are the unprocessed ones (without the watermark) which is visible in the preview,

the FilePond component :

<FilePond ref={ref} files={imageFiles} onupdatefiles={setImageFiles} allowMultiple={true} maxFiles={15} allowReorder dropValidation name="files" / sets the file input name, it's filepond by default / labelIdle='Drag & Drop your files or Browse' //FileTypeValidation plugin allowFileTypeValidation labelFileTypeNotAllowed="File of invalid type" imagePreviewMaxFileSize="6MB" acceptedFileTypes={["image/*"]} //FileSizeValidation plugin

allowFileSizeValidation minFileSize="100KB" maxFileSize="6MB" labelMaxFileSizeExceeded="File is too large" labelMaxFileSize="Maximum file size is {filesize}" // allowFileMetadata // allowImageTransform imageTransformClientTransforms imageTransformCanvasMemoryLimit

fileMetadataObject={{ markup: [ [ "rect", { left: 0, right: 0, bottom: 0, height: "100px", backgroundColor: "rgba(0,0,0,.5)", }, ], [ "image", { right: "10px", top: "10px", width: "60px", height: "60px", src: logo, fit: "contain", }, ], ],

onpreparefile: (file, output) => { console.log(output);

}, }}

     imageTransformBeforeCreateBlob={(canvas) =>
       new Promise((resolve) => {
         // Do something with the canvas, like drawing some text on it
         const ctx = canvas.getContext("2d");
         ctx.font = "48px serif";
         ctx.fillText("Bdoon Wasyt", 10, 50);

         console.log("imageTransformBeforeCreateBlob", ctx, canvas);

         // return canvas to the plugin for further processing
         resolve(canvas);
         imgy = canvas
       })
     }
     imageTransformAfterCreateBlob={(blob) =>
       new Promise((resolve) => {
         // do something with the blob, for instance send it to a custom compression alogrithm
         console.log("imageTransformAfterCreateBlob", blob);

         // return the blob to the plugin for further processing
         resolve(blob);
       })
     }

/>

in the upload function i access the files using:

      let file = await ref.current.getFile(i).file

also imageTransformBeforeCreateBlob doesn't add text

i appreciate help fixing this.

IlyasMohetna commented 8 months ago

While this question might be a bit dated, here's a response for individuals currently encountering this issue.

I don't know why I saw a lot of people saying that this is not possible. There is a workaround without limitations. If you have data, you just want to put it somewhere else.

Solution :

  1. In the form you want to submit create an input

    • If it's multiple make sure you put the brackets for the name

      <input type="file" id="files" name="files[]" multiple>
    • If it's not multiple

      <input type="file" id="files" name="files">
  2. Create your pond instance

    const pond = FilePond.create(element);
  3. Attach an event and call a function

    pond.on('addfile', (error, file) => {
    PutIntoInput();
    }
  4. Create the function

    function PutIntoInput(){
       const filesFromPond = pond.getFiles(); // pond is your instance
       const filesInput = $('#files')[0]; // element we created in step1
       const newFileList = new DataTransfer(); // new file list
       filesFromPond.forEach(file => { // Add each file to a list
           newFileList.items.add(file.file);
       });
       filesInput.files = newFileList.files; // Set the file list
    }

PS : You might use CSS to hide the input type="file" because you cannot set it to hidden in this case.