Leveraging JS File Uploader Libraries in PHP Forms: Example Using FilePond

Jim Dee
Jim Dee
Jul 8 · 9 min read

I ran into a situation recently where a client wanted a web page similar to an AirBnb-type page where they wanted to show numerous slideshows on a single web page (both integrated into a Google Maps map and also on other parts of the page).

One major consideration whenever you’re loading, say, 100 photos on a single page is bandwidth. You certainly don’t want someone going out on their new phone and snapping 100 photos at 4 or 5 megs each, and then uploading them and expecting the page to load quickly (if at all — and not to mention mobile performance).

Further complicating things, some of the slideshows needed to be small for this page, but others (using the same photos) needed to be different sizes. So, I needed various specific-sized versions of each photo as well, all optimized for filesize and quality.

Even further complicating the issue was the fact that they’re on shared hosting and I didn’t want to use that kind of a server for a lot of CPU-intensive graphics work. Nor did I like the idea of requiring the client to do a bunch of Photoshopping and FTP-ing files around.

Clearly, this meant considering some client-side Javascript solutions, of which there are a good number available. A few I saw were:

Please note: I’m not affiliated with any of them, and I expect there are other solutions available as well. But, for the purposes here, I’ll share my experiences with FilePond, authored by a guy named Rik Schennink from The Netherlands. My rationale for selecting FilePond to work with over the others was simply a few items:

  • I saw that it can optimize the images and create the transformed versions I wanted;
  • The time in between (a) throwing some demo code on a server and (b) actually getting some usable results to build from … was the best out of the above. (Your results may vary. And, full disclosure: I’m not the best JS guy in the world, as I spend about 90% of my time on server-side PHP and database applications!)

Anyway, while FilePond does indeed do all that it claims, there weren’t a ton of examples online showing how one might integrate this into a normal PHP form, which is what I needed to do. So, below, I’ll paste the code of an entire web page — very simplified — that shows the basics of this type of integration, and then I’ll talk about it a bit more below.

<?php    // RECEIVE UPLOADS:
$isFileUploadRequest = $_POST['sentPhotos'];
if ($isFileUploadRequest) {

$filePondArray = $_POST['filepond'];

$baseFileLocation = '/your/server/image/filepath/';
$numFilePondObjects = sizeof($filePondArray);
if ( !$numFilePondObjects ) { die('No photos sent!'); }

echo '<b>You sent '.$numFilePondObjects.' pics. Each pic has 3 versions.</b><br>';

// iterate through the objects...
for ($xx=0; $xx<$numFilePondObjects; $xx++) {

$thisFilePondJSON_object = $filePondArray[$xx];

$thisFilePondArray = json_decode($thisFilePondJSON_object, true);

// isolate the encoded pics...
$thisFilePondArray_picData = $thisFilePondArray['data'];
$thisFilePondArray_numPics = sizeof($thisFilePondArray_picData);

// iterate through pics in this object...
for ($yy=0; $yy<$thisFilePondArray_numPics; $yy++){

$thisPic_PhotoNumber = $xx + 1;

$thisPic_version = $thisFilePondArray_picData[$yy]['name'];
if (!$thisPic_version) { $thisPic_version = 'v1_50px'; }
$thisPic_name_temp = 'photo_' . $thisPic_PhotoNumber . '_'. $thisPic_version .'.jpg';

$thisPic_encodedData = $thisFilePondArray_picData[$yy]['data'];
$thisPic_decodedData = base64_decode($thisPic_encodedData);

$thisPic_fullPathAndName = $baseFileLocation . $thisPic_name_temp;
echo '<br>Photo will save as: <b>'. $thisPic_fullPathAndName .'</b>';
//write the pic here
//file_put_contents($thisPic_fullPathAndName, $thisPic_decodedData);

}

echo '<br>';

}
}
?>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8" />
<title>FilePond Test</title>
<link rel='stylesheet' href=' />
<link rel='stylesheet' href=' />
<style>.fileBox{ width: 80%; margin-left: auto; margin-right: auto; margin-top: 40px; background: #fbfbb8; padding: 20px; border: 3px solid black;}</style>
</head><body><div class="fileBox">

<form method="POST" action="here put the URL of your form]" enctype="multipart/form-data">

<input type="file" class="filepond" name="filepond[]" multiple data-max-file-size="6MB" data-max-files="5" />
<input type="submit" value="Upload Photo(s)" name="B1" class="btn btn-info" />
<input type="hidden" name="sentPhotos" value="1" />

</form>
</div><script src='></script>
<script src='></script>
<script src=""></script>
<script src='></script>
<script src=""></script>
<script src=""></script>
<script src=""></script>
<script src='></script>
<script src='></script>
<script>
// register desired plugins...
FilePond.registerPlugin(
// encodes the file as base64 data...
FilePondPluginFileEncode,
// validates the size of the file...
FilePondPluginFileValidateSize,

// validates the file type...
FilePondPluginFileValidateType,
// corrects mobile image orientation...
FilePondPluginImageExifOrientation,

// calculates & dds cropping info based on the input image dimensions and the set crop ratio
FilePondPluginImageCrop,

// calculates & adds resize information
FilePondPluginImageResize,

// applies the image modifications supplied by the Image crop and Image resize plugins before the image is uploaded
FilePondPluginImageTransform,
// previews dropped images...
FilePondPluginImagePreview
);// Select the file input and use create() to turn it into a pond
FilePond.create( document.querySelector('.filepond'), {

allowMultiple: true,
allowFileEncode: true,
maxFiles:5,
required: true,
maxParallelUploads:5,
instantUpload:false,
acceptedFileTypes: ['image/*'],
imageResizeTargetWidth: 50,
//imageResizeMode: 'contain',
imageCropAspectRatio: '1:1',
imageTransformVariants: {
'v2_100px': transforms =>
{
transforms.resize.size.width = 100;
return transforms;
},
'v3_200px': transforms => {
transforms.resize.size.width = 200;
return transforms;
}
},
imageTransformOutputQuality: 50,
imageTransformOutputMimeType: 'image/jpeg',

onaddfile: (err, fileItem) => {
console.log(err, fileItem.getMetadata('resize'));
},

// alter the output property
onpreparefile: (fileItem, outputFiles) => {
// loop over the outputFiles array
outputFiles.forEach(output => {
const img = new Image();
// output now is an object containing a `name` and a `file` property, we only need the `file`
img.src = URL.createObjectURL(output.file);
document.body.appendChild(img);
})
}

});
</script>
</body>

</html>

What’s Going On

Let’s start with the HTML part of the script. As you can see, in the <head>, I’m’ loading some CSS from FilePond’s CDN. You can also of course save those files locally and serve them locally. That’s what I wound up doing on my production project. The <style> is there simply for some prettifying so the below screenshots will look good as I talk about this.

In the <form> itself, note that the <input> is written like so:

<input type="file" class="filepond" name="filepond[]" multiple data-max-file-size="6MB" data-max-files="5" />

That “filepond[]” part is important and took me a while to get right. I’d originally named the field just “filepond,” which wasn’t cutting it for multiple submissions (as then the multiples would not come through in the $_POST). So, if you’re allowing multiple file uploads (or even if you’re not), this works well.

You can then see all of the various scripts needed for the FilePond plugins I’ve elected to leverage. Again, I wound up serving them locally, but this also works. I believe the ordering also is maybe not *critical*, but the above does work well.

After that comes the custom <script> areas where you do stuff and create the FilePond. In the first one, we’re registering the needed plugins. I recommend for in-depth explanations of the functionality and methods available.

Down in the “FilePond.create” section, things get a little more tricky. This is the part that gave me the most trouble — not that it’ll give you trouble necessarily. But, I did find some sensitivity in this area. Most notably, for me, was the image transform areas. Some of the sample code floating around out there didn’t work well for me without some tweaking — sometimes just little things fairly-obvious like the image transform names needing quotes.

Other items weren’t as clear. For example, I found that, once I set the “imageResizeTargetWidth” param, I couldn’t get any transformed photos to come out smaller. If I set “imageResizeTargetWidth” to 500 (as in px), then my other two transforms would also come out as 500px, even if I’d set them to 100 and 200. But, they *would* upscale to, say, 1000px.

I managed to talk to the developer, Rik, about this. He told me that this relates to the “imageResizeMode” param going to “cover” by default, and recommended that I set it to “contain” instead. (It’s in my sample code, above, as a reminder, though it’s commented out.) That still didn’t quite solve it for me, but I probably did something wrong. And, besides, I actually got it working to my needs by simply reversing the order of things — setting “imageResizeTargetWidth” to the smallest size I needed and then having my two transforms get larger each time.

For example, using the exact code I posted… When I drop some cat pics on there, you see the 3 generated pics below, properly sized at 50, 100, and 200px, for each one. (My production model uses 100, 300, and 1000px — and also works great!)

BTW, you can remove this whole part (below), which I’d put in for troubleshooting purposes, based on the FilePond author’s recommendations in an article he wrote (). It simply logs some info into the console and also posts the pre-sized previews onto the DOM as you’re working — both not necessary for production, but handy for dev work.

Anyway, what happens for PHP forms (using the code supplied) is that when you upload the photos, FilePond creates base64 encoded data for each one and passes that data along in the $_POST. (Note: You do not need to have FilePond base64 encode the files. That is total optional as a plugin, but this works very well when you’re integrating FilePond into a PHP form and simply want the pics sent as files in a $_POST.)

So, if I send those two photos, my $_POST will get back two objects in the “filepond” item.

Or, to show that more clearly… Let’s say I had an input field named “yourname”, and then the FilePond input with it’s two photos. The $_POST would look like this:

So, once you get your $_POST back, it’s simply a matter of validating your submissions, parsing the data as needed (your pics now residing in that “filepond” item, in two JSON wads), base64 decoding the photos (or other types of data you’re working with), and saving them somewhere (e.g., with PHP’s file_put_contents()). The sample code shows a very basic example of this, but you’ll find your production code probably gets much more complicated to fit your needs.

Results:

It took some work, but I have success on all of the following:

  • Accepts multiple files (photos in my case);
  • Crops them all to the size aspect ratio desired (the imageCropAspectRatio: ‘1:1’ setting in my code);
  • Generates two variants (and you can generate more or less if you need to) at the sizes I need;
  • All created files are at the quality setting I desire (mine is set to 50 to help keep file sizes reasonable / optimized for fast loading);
  • Also saves all of its files as JPGs. So, I can throw any source files at it (PNG etc.), but they all come back as JPG, in this case.

Notes: Improvements / Customizations, etc.

Working with these libraries, post-upload, you’ll need to be able to programmatically take the ball and run with it a good bit, as these uploaders are solely focused on getting files uploaded. After that, you of course need to do any additional coding yourself related to your uploaded files. File naming and other file management best practices come to mind, of course.

In my own production model, after upload & saving, I also do a quick redirect to the same page to clear out the $_POST so that people can’t inadvertently reload the page and re-upload the same photo data. (An old trick, actually.)


🤖 heads up Array Web Development, LLC, in Portland, Oregon. When he is not coding database applications, he writes articles for his web design blog, “,” for his personal blog, “,” and for various other publications on Medium.com. You can reach him at: Jim [at] ArrayWebDevelopment.com.

Web Designer / Web Developer Magazine

WDWD Magazine features articles on web site design / development, internet marketing, social media, SEO, and topics like marketing, communications, business development, etc. Editor: Jim Dee of Array Web Development — jim@arraywebdevelopment.com.

Thanks to codernoon.

Jim Dee

Written by

Jim Dee

Principal at Array Web Development http://www.arraywebdevelopment.com/ in Portland, OR.

Web Designer / Web Developer Magazine

WDWD Magazine features articles on web site design / development, internet marketing, social media, SEO, and topics like marketing, communications, business development, etc. Editor: Jim Dee of Array Web Development — jim@arraywebdevelopment.com.