To be able to have an ACF gallery field automatically generate a zip file of all the images uploaded is a useful feature.
Note: I’ve modified this code to take into account a pre-existing zip file of images, see end of article for this modified code.
Luckily someone has already completed the code. The devs linked blog is no longer active which is unfortunate as he cites a cautionary note in his code comments. His blog post is reproduced below care of the Way Back Machine…
A recent client project for a photographer involved creating protected pages where their clients could download an entire gallery of images as a zip folder. The galleries are dynamic, so I wanted to make the download links dynamic as well, so that every time they added an image to the gallery it would get added to the download without any extra work.
I ended up building out the functionality using a bunch of custom PHP along with an Advanced Custom Fields gallery field to make it happen. As it turned out, we went a different direction for the final build (they wanted granular control over the resolution and a few other things that ended up making it more reasonable to lose the dynamic link).
I thought it’d still be fun to share the code since it seems like a pretty useful utility, so here goes!
Full Code
Let’s just start off with the full code so you can see it all in context. I’ve put a lot of comments throughout to narrate what’s going on and then I’ll point out a few key pieces and features in the breakdown below.
<?php // Check the gallery field (created with ACF PRO) for images $images = get_field('images'); if( $images ) : // If it does have images: // Create variable for download file // This uses a /downloads/ directory and the page title as the file name $destination = 'downloads/' . sanitize_title( get_the_title() ) . '.zip'; // Check if the file already exists (see tutorial for a cautionary note) if ( file_exists( $destination ) ) { // If it exists, print the download link echo '<a href="' . esc_url( home_url( '/' ) ) . $destination . '" class="download-link" download>DOWNLOAD ALL</a>'; } else { // If the file doesn't already exist, create the file $files = array(); foreach( $images as $singlefile ) { // create an array of the image files in the gallery $files[] = get_attached_file( $singlefile['ID'] ); } if( count( $files ) ) { // Check if there are files in the array (files exist) // If there are files, create a zip file in the location specified $zip = new ZipArchive(); $zip->open( $destination, ZipArchive::CREATE ); foreach( $files as $file ) { // for every file in the array if ( file_exists($file) ) { // if the file actually exists, add it to the zip file $new_filename = substr($file,strrpos($file,'/') + 1); $zip->addFile( $file, $new_filename ); } } // Once you've got all the files, close out the zip $zip->close(); // Then link to the file you just created echo '<a href="' . esc_url( home_url( '/' ) ) . $destination . '" class="download-link" download>DOWNLOAD ALL</a>'; } else { // No images are found, display an error message echo 'no files found'; } } ?> <!-- Next, display the on-page gallery --> <div class="gallery-grid"> <?php foreach( $images as $image ) : // For every image in the gallery, display the thumbnail linked to a larger image for a lightbox ?> <a href="<?php echo $image['url']; ?>" title="<?php echo $image['title']; ?>" rel="gallery"> <img src="<?php echo $image['sizes']['thumbnail']; ?>" alt="<?php echo $image['alt']; ?>" /> </a> <?php endforeach; ?> </div><!-- / .gallery-grid --> <?php else : // If the gallery field is empty: ?> <p>No images found.</p> <?php endif; ?>
This whole operation can be broken down into three chunks:
- Checking the custom field for images
- Generating the zip and download link
- Displaying the gallery grid
Let’s take them piece by piece:
The custom field check wraps around the rest of the code, since we don’t want to show any of the rest of it if there aren’t images in the gallery, so let’s look at that first.
1. Checking the ACF gallery field for images
I started out by creating an ACF gallery field (part of Advanced Custom Fields PRO) called “Images” The outer frame of the code snippet involves getting the field data and using a conditional to check if the field contains images:
<?php $images = get_field('images'); if( $images ) : ?> <!-- download link and gallery display --> <?php else : ?> <p>No images found.</p> <?php endif; ?>
All the code for the download link and gallery itself goes in the commented area (the “yes, field has images” section) and there’s a message to display if no images are found.
2. Generating the zip and download link
The bulk of the code that goes in that comment area is for the zip file generation and the download link.
To start out, I created a variable to hold the download link for the file that will be generated. This is a little tricky – it needs to be a place on the server that is accessible/writeable, so it may take some trial and error to get it right. I used a special /downloads/ directory relative to the root.
$destination = 'downloads/' . sanitize_title( get_the_title() ) . '.zip';
The sanitize_title( get_the_title() ) bit uses the gallery page title as the file name, which is a nice little feature to make the file easy to identify after it is downloaded.
Now I can use that variable of $destination to create my file and links throughout the rest of the zip file code.
Next up, there’s a conditional to check if there’s already a zip file in that location. If the file does exist already, we just display the download link, but if not we have to create the file before displaying the link.
<?php if ( file_exists( $destination ) ) { echo '<a href="' . esc_url( home_url( '/' ) ) . $destination . '" class="download-link" download>DOWNLOAD ALL</a>'; } else { // Code to generate and then display the link } ?>
Caution: One thing I didn’t get around to testing before we went in a different direction was whether this check causes problems if the gallery is updated. I imagine it would since the file would already exist but with the old set of images.
You may find that you need to add some kind of dynamic text to the file name to make sure it gets updated regularly. The date could work, although you may not want to generate a new zip every day, so maybe use the week number or month or something. Or, potentially something using a cron job to regularly clear out the folder, as discussed in this Stack Overflow thread.
I’m not going to go line by line through generating the zip file since it’s commented in the gist, but the general flow is that you create an array of the gallery files, and then create the zip file from that array of files.
Remember that we’re tying into that ACF gallery field, so if you do this with a different method for inputting your files, you’ll have to change the beginning loop:
foreach( $images as $singlefile ) { $files[] = get_attached_file( $singlefile['ID'] ); }
In this little snippet, $images is our array from the gallery field, the same variable we used in the very first line of the code where we got the data from that field and checked to be sure there were images in the gallery.
There are a few checks throughout the sequence, making sure that there are files in the array, that each file exists, etc., and then when all is said and done the zip file is created and a download link generated.
3. Displaying the gallery grid
Finally, there’s the on-page gallery display. We’re looping through the gallery field again and spitting out the data into a grid view where the thumbnails are linked to full size images for a lightbox display. This is all ACF syntax for gallery fields, nothing really customized in this bit.
<div class="gallery-grid"> <?php foreach( $images as $image ) : ?> <a href="<?php echo $image['url']; ?>" title="<?php echo $image['title']; ?>" rel="gallery"> <img src="<?php echo $image['sizes']['thumbnail']; ?>" alt="<?php echo $image['alt']; ?>" /> </a> <?php endforeach; ?> </div><!-- / .gallery-grid -->
Modified code
The code below takes into account an already existing ZIP file. It counts the number of files in the existing zip and compares this against the actual number of files in the image gallery
<?php // Check the gallery field (created with ACF PRO) for images $images = get_field('image_bank'); if( $images ) : // If it does have images: // Create variable for download file // This uses a /image-bank/ directory and the stock code + page title as the file name $destination = 'image-bank/' . get_field('stock_code') . '-' . sanitize_title( get_the_title() ) . '.zip'; $zip = new ZipArchive(); if ( file_exists( $destination ) ) { $zip->open( $destination ); $ria_num_zip_files = $zip->numFiles; $files = array(); foreach( $images as $singlefile ) { // create an array of the image files in the gallery $files[] = get_attached_file( $singlefile['ID'] ); } $ria_new_gallery_images = count( $files ); if ( $ria_num_zip_files != $ria_new_gallery_images ) { unlink( $destination ); $zip->open( $destination, ZipArchive::CREATE ); foreach( $files as $file ) { // for every file in the array if ( file_exists($file) ) { // if the file actually exists, add it to the zip file $new_filename = substr($file,strrpos($file,'/') + 1); $zip->addFile( $file, $new_filename ); } } // Once you've got all the files, close out the zip $zip->close(); } echo '<p><a href="' . esc_url( home_url( '/' ) ) . $destination . '" class="download-link" download>DOWNLOAD ALL IMAGES</a>'; clearstatcache(); $rai_zip_fileize = filesize( realpath($destination) ); echo ' Size: ' . formatSizeUnits( $rai_zip_fileize ) . '</p>'; } ?>
4 Comments
Jerome
March 14, 2020 at 12:41 amI’m sorry, but where do I put this once I’ve adapted it for myself?
Oliver Partridge
March 17, 2020 at 8:36 amPut the code into the template file that is displaying the page with your gallery on. So you might have a lage template called page-gallery.php
Hiren
April 27, 2020 at 7:10 amI just started doing something similar and came across this post whilst Googling.
Do you know if it is possible to generate the zip when a download button is clicked?
Also, instead of checking the number of files etc, I was going to save my zip files with the following naming convention:
$post_author_$post_id_$post_datemodified.zip
That way, when the download button is clicked, I can build the filename up again dynamically:
get_the_author_meta(‘display_name’);
get_the_id();
get_the_modified_time(‘U’);
And check to see if it exists. If it doesn’t, then create the zip.
What do you think?
Oliver Partridge
April 27, 2020 at 7:35 amI don’t know Hiren, you would probably need some javascript to interact with the button click and trigger the zip file generation. The filename generation using a date makes a lot more sense than checking the number of images though