I want to upload my custom image sizes in custom folders. The folder should have the name of the selected width. For example:
If I add these custom sizes…
add_image_size('custom-1', 300, 9999);
add_image_size('custom-2', 400, 9999);
It would be nice the uploaded images are uploaded like this:
http://www.my-site.com/wp-content/uploads/300/my-image.jpg
http://www.my-site.com/wp-content/uploads/400/my-image.jpg
Is this possible? I’ve only found that I can change the global upload folder with the upload_dir filter.
Philipp, anything is possible if you set your mind to it. You can solve your issue by extending the WordPress image editor class.
Note I’m using WordPress 3.7 – I haven’t checked any of the below code in earlier versions and in the latest 3.8 release.
Image Editor basics
WordPress has two built in classes that handle image manipulation:
WP_Image_Editor_GD
(/wp-includes/class-wp-image-editor-gd.php
)WP_Image_Editor_Imagick
(/wp-includes/class-wp-image-editor-imagick.php
)These two classes extend
WP_Image_Editor
because they both use a different image engine (GD and ImageMagick respectively) to load, resize, compress and save images.By default WordPress will try to use the ImageMagick engine first, which needs a PHP extension, because it is generally preferred over PHP’s default GD engine. Most shared servers don’t have the ImageMagick extension enabled though.
Add an Image Editor
To decide which engine to use, WordPress calls an internal function
__wp_image_editor_choose()
(located in/wp-includes/media.php
). This function loops through all engines to see which engine can handle the request.The function also has a filter called
wp_image_editors
that allows you to add more image editors like so:Note we’re prepending our custom image editor class
WP_Image_Editor_Custom
so WordPress will check if our engine can handle resizing before testing other engines.Creating our Image Editor
Now we’re gonna write our own image editor so we can decide on filenames for ourselves. Filenaming is handled by the method
WP_Image_Editor::generate_filename()
(both engines inherit this method), so we should overwrite that in our custom class.Since we only plan on changing filenames, we should extend one of the existing engines so we don’t have to reinvent the wheel. I will extend
WP_Image_Editor_GD
in my example, as you probably don’t have the ImageMagick extension enabled. The code is interchangeable for an ImageMagick setup though. You could add both if you’re planning on using the theme on different setups.Most of the code above was directly copied from the
WP_Image_Editor
class and commented for your convenience. The only actual change is that the suffix is now a prefix.Alternatively, you could just call
parent::generate_filename()
and use anmb_str_replace()
to change the suffix into a prefix, but I figured that would be more inclined to go wrong.Saving new paths to metadata
After uploading
image.jpg
, the uploads folder looks like this:2013/12/150x150/image.jpg
2013/12/300x300/image.jpg
2013/12/image.jpg
So far so good. However, when calling basic functions like
wp_get_attachment_image_src()
, we’ll notice all image sizes are stored asimage.jpg
without the new directory path.We can work around this issue by saving the new folder structure to the image metadata (where the filenames are stored). The data runs through various filters (
wp_generate_attachment_metadata
among others) before being inserted into the database, but since we’re already implementing a custom image editor, we can travel back to the source of image size metadata:WP_Image_Editor::multi_resize()
. It generates arrays like this one:We’ll overwrite the
multi_resize()
method in our custom class:As you can see, I didn’t bother replacing any of the code. I just call the parent method and let it generate the metadata. Then I loop through the resulting array and adjust the
file
value for each size.Now
wp_get_attachment_image_src($att_id, array(300, 300))
returns2013/12/300x300/image.jpg
. Hooray!Final thoughts
I hope this provided a good basis for you to elaborate on. However, please note if an image is smaller than the specified size (e.g. 280×300), the generated suffix (prefix in our case) and image sizes are 280×300, not 300×300. If you upload a lot of smaller images, you’ll get a lot of different folders.
A good solution would be to either use the size slug as a folder name (
small
,medium
, et cetera) or expand the code to round sizes up to the nearest preferred image size.You noted you want to use just the width as a directory name. Be warned though – plugins or themes could generate two different sizes with the same width but different heights.
Also, you can remove the year/month folders either by disabling ‘Organize my uploads into month- and year-based folders’ under Settings > Media or by manipulating
generate_filename
even further.Hope this helps. Good luck!
@Robbert’s answer was a divine resource in my efforts to store alternate sizes generated by WordPress in separate directories. My code also changes the upload directory to ./media so make sure to edit these lines if you don’t want that. It’s not an exact answer to the first poster’s question, but offers an alternative solution to the same problem:
Works without any problems according to my tests, although I haven’t tried to check how it fares with popular gallery/media plugins.
related bonus: a raw utility to delete all WordPress generated thumbnails
delete_deprecated_thumbs.php
The answer @robbert is a great, great answer. However, it’s a little dated (I’m using WordPress 5.7) and I wanted to do this exact same thing, so here’s a slightly updated answer with a few extras:
I placed these all in the same file, as a plugin.
Adding both of the new editors.
The GD Class extension
The IMagick class extension
Update the attachment metadata
Now, sub-directories with the media size name will be created within the uploads folder, e.g.
/uploads/photo.jpg
will have/uploads/thumbnail/photo.jpg
,/uploads/medium/photo.jpg
, etc…But not only that, deletion will remove those attachments too. (Plus, if the folder becomes empty, that will be removed as well).
Link to Plugin
For the code: Github – IORoot
Details – No need to read
For those who are curious, the flow for the deletion will be:
post.php
will runwp_delete_attachment()
function.wp_get_attachment_metadata
function inpost.php
will run thewp_get_attachment_metadata
filter.$data['sizes']
array and return it back.$data
array is passed back to set$meta
inwp_delete_attachment()
.$meta
array is then used at the bottom of thewp_delete_attachment()
function, on the line:wp_delete_attachment_files()
function will use this$meta
array with aforeach()
to loop through the sizes and delete each filename (which we updated to include the slug directory).And that’s it. This will see the updated
$sizes
array (within$meta
) which we updated from:to become:
I’ve looked at these parts of WordPress code and I’m afraid I don’t have any good news.
There are 2 classes:
WP_Image_Editor_GD
WP_Image_Editor_Imagick
,both extending abstract
WP_Image_Editor
class.These classes are implementing
multi_resize
method, which is used to generate multiple images from uploaded one.The really bad news is that there are no filter hooks in there, that we could use to modify destination path for newly created files.
Ok, I think i got it! Not perfect but okay for this I wanted it for. For me only the width of an image is important. Height is useless for me. Especially for implementing Imager.js the height in the image url is disturbing.
With this code the filenames are like:
It’s not possible to add a subfolder to the filenames, because if I add images in a post/page always the original source will used instead. And removing these images on deleting will also not working. I’m not sure why.