How to reorganize uploaded medias in WordPress
WordPress does not offer much control over the uploaded medias by default. The only available option is a checkbox in the media settings page, which allows up to store the uploads in separate folders, based on the upload date.
We are going to see how to customize this, from changing the folder’s name to fully redefining it’s structure. First of all, make sure you have a backup of your website, both database and files, in case anything goes wrong.
Uploads Folder
WordPress stores the uploaded medias under the wp-content/uploads
folder by default. This path is defined by a constant, only if not defined before. Hence, we can define our own folder to be used for storing the uploaded media. To change the folder that is used for this purpose, add this line to your wp-config.php
file:
define( 'UPLOADS', WP_CONTENT_DIR . '/my-uploads' );
This will tell the WordPress to store the uploads under the my-uploads
directory, so the upload destination would be wp-content/my-uploads
. If you wish to store the uploads directly in the root folder outside the wp-content
directory, you can remove the WP_CONTENT_DIR
constant:
define( 'UPLOADS', 'my-uploads' );
Note that we don’t need to add a path separator anymore.
Thumbnail Generation
One of the most recognized facts about WordPress’s media management is the way it manages the thumbnails. By default, the thumbnails are generated with the same name as the original image, while having a size suffix added to their names. So, a file that is named sample.jpg
will have a thumbnail named sample-150x150.jpg
in a blank installation.
But how does this happen? For this, we need to dig inside some of the core files. WordPress uses the classes named
and WP_Image_Editor
WP_Image_Editor_GD
to handle the thumbnail generation . These classes can be extended, and thus we can use our own methods to handle this. These classes are stored inside the wp-includes
folder:
class-wp-image-editor.php
class-wp-image-editor-gd.php
There are various methods inside these classes, but the one that we are interested in is the
method. generate_filename()
Before we begin, we need to tell WordPress to use our own custom image editor, and also include these files inside our project. To add our own image editor, we can hook into the
filter inside our plugin or theme’s wp_image_editors
functions.php
file:
add_filter('wp_image_editors', 'my_wp_image_editors');
function my_wp_image_editors($editors) {
array_unshift($editors, "my_WP_Image_Editor");
return $editors;
}
Which the my_WP_Image_Editor
is the name of our custom class, used to extend the original class. Now let’s include the image editor classes too, but we don’t want to include them multiple times and cause a fatal error:
require_once ABSPATH . WPINC . "/class-wp-image-editor.php";
require_once ABSPATH . WPINC . "/class-wp-image-editor-gd.php";
We’re good to go now. Here’s the default method that is used to generate the filename for the thumbnails:
public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) {
// $suffix will be appended to the destination filename, just before the extension
if ( ! $suffix )
$suffix = $this->get_suffix();
$dir = pathinfo( $this->file, PATHINFO_DIRNAME );
$ext = pathinfo( $this->file, PATHINFO_EXTENSION );
$name = wp_basename( $this->file, ".$ext" );
$new_ext = strtolower( $extension ? $extension : $ext );
if ( ! is_null( $dest_path ) && $_dest_path = realpath( $dest_path ) )
$dir = $_dest_path;
return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}";
}
What this method does is:
- Check if a suffix has already been passed to it. If not, generate a suffix for the file using the
get_suffix()
method. - Get the directory this file will be stored in, alongside it’s extension.
- Get the file’s base name. Now that is the file’s name without extension, for example, the base name of
sample.jpg
file issample
. - If an extension has been passed by the method that internally called this method, set it as the new extension.
- If a custom path has been passed to this method, and is valid, use it as the directory that stored the file.
- Put all the generated content above together, creating a final path and file name.
We can manipulate the above, and store the thumbnails in any structure we want. The get_suffix()
method that is responsible for generating the thumbnail’s suffix is as below:
public function get_suffix() {
if ( ! $this->get_size() )
return false;
return "{$this->size['width']}x{$this->size['height']}";
}
This is where the thumbnails get their width×height
suffix from. As an example, I’m going to store the thumbnails inside a folder named thumbnails
, inside the my-uploads
directory, and generate a random name for the file using an md5 hash:
// A property to store the thumbnail's folder name. Optional.
private $MY_THUMBNAIL_DIRECTORY = 'thumbnails';
// We override the method that generates the filenames
public function generate_filename( $suffix = NULL, $dest_path = NULL, $extension = NULL ) {
$dir = pathinfo( $this->file, PATHINFO_DIRNAME );
$ext = pathinfo( $this->file, PATHINFO_EXTENSION );
$new_ext = strtolower( $extension ? $extension : $ext );
if ( ! is_null( $dest_path ) && $_dest_path = realpath( $dest_path ) ) {
$dir = $_dest_path;
}
$name = md5( uniqid() );
return trailingslashit( $dir ) . $this->MY_THUMBNAIL_DIRECTORY . "/{$name}.{$new_ext}";
}
Now you’re thumbnail will be stored similar to below:
/my-uploads/thumbnails/1a79a4d60de6718e8e5b326e338ae533.jpg
The first thing that comes into mind is the collision chance of the names. Md5 hashes are not truly unique, and you might end up having the same thumbnail for different images. Although the changes are pretty slim, about once in every 340282366920938463463374607431768211456 files. Now that is 2^128. If that’s not accurate enough for you ( it is for me! ), you can do something like this to check if the file already exists, and generate another hash:
do {
$name = md5( uniqid() );
} while ( file_exists( trailingslashit( $dir ) . $this->MY_THUMBNAIL_DIRECTORY . "/{$name}.{$new_ext}" ) );
But you will end up having extra thumbnails in your uploads, when trying to regenerate the thumbnails without clearing the existing ones first.
As you can see, there are lots of ways to play with these methods, and lots of reasons too. Maybe you have a website that publishes copyrighted content, and you want to watermark and store your thumbnails in another directory, and then block the original unprotected files using a .htaccess
file. This can not be done easily in the default structure.
For more complicated examples about this matter, you can also check out my post on WordPress StackExchange website. Feel free to let me know what you think about this, or if you have any suggestions!