An step by step guide to fully customize the way WordPress manages the uploaded medias.

Jack Johansson
Jack Johansson

Freelance Web Developer

December 31, 2018

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/uploadsfolder 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.phpfile:

define( 'UPLOADS', WP_CONTENT_DIR . '/my-uploads' );

This will tell the WordPress to store the uploads under the my-uploadsdirectory, 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-contentdirectory, you can remove the WP_CONTENT_DIRconstant:

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.jpgwill have a thumbnail named sample-150x150.jpgin 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 WP_Image_Editorand 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-includesfolder:

  • 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 thegenerate_filename() method.

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 wp_image_editorsfilter inside our plugin or theme’s functions.phpfile:

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_Editoris 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.jpgfile is sample.
  • 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×heightsuffix from. As an example, I’m going to store the thumbnails inside a folder named thumbnails, inside the my-uploadsdirectory, 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:


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 .htaccessfile. 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!

Jack Johansson
Jack Johansson

Freelance Web Developer

December 31, 2018