7

I want to store all attachments (media) in folders named with each post title.

Example: /wp-content/uploads/my-post-about-stuff/

The folder should contain all media attached to that post.

Is that possible?

1
  • Does anyone know about some plugin for doing this?
    – user3047
    Aug 16, 2011 at 23:11

3 Answers 3

11

Disclaimer

  • Tracking the way to do the same using the post_id
  • Having already answered one question about separating PDF's from the rest
  • Found disperse questions about this matter
  • Decided to adopt this Q as the venue to publish this.

Follow the instructions in the comments, and this code can be adapted to organize the uploads folder by post_name (aka: slug), post_author, post_id or media type.

add_filter('wp_handle_upload_prefilter', 'wpse_25894_handle_upload_prefilter');
add_filter('wp_handle_upload', 'wpse_25894_handle_upload');

function wpse_25894_handle_upload_prefilter( $file )
{
    add_filter('upload_dir', 'wpse_25894_custom_upload_dir');
    return $file;
}

function wpse_25894_handle_upload( $fileinfo )
{
    remove_filter('upload_dir', 'wpse_25894_custom_upload_dir');
    return $fileinfo;
}

function wpse_25894_custom_upload_dir($path)
{   
    /*
     * Determines if uploading from inside a post/page/cpt - if not, default Upload folder is used
     */
    $use_default_dir = ( isset($_REQUEST['post_id'] ) && $_REQUEST['post_id'] == 0 ) ? true : false; 
    if( !empty( $path['error'] ) || $use_default_dir )
        return $path; //error or uploading not from a post/page/cpt 
    
    /*
     * Save uploads in ID based folders 
     *
     */
    
    /*
     $customdir = '/' . $_REQUEST['post_id'];
    */


    /*
     * Save uploads in SLUG based folders 
     *
     */
    
     $the_post = get_post($_REQUEST['post_id']);
     $customdir = '/' . $the_post->post_name;


    /*
     * Save uploads in AUTHOR based folders 
     *
     * ATTENTION, CAUTION REQUIRED: 
     * This one may have security implications as you will be exposing the user names in the media paths
     * Here, the *display_name* is being used, but normally it is the same as *user_login*
     *
     * The right thing to do would be making the first/last name mandatories
     * And use:
     * $customdir = '/' . $the_author->first_name . $the_author->last_name;
     *
     */
    
    /* 
      $the_post = get_post($_REQUEST['post_id']);
      $the_author = get_user_by('id', $the_post->post_author);
      $customdir = '/' . $the_author->data->display_name;
    */
    
    
    /*
     * Save uploads in FILETYPE based folders 
     * when using this method, you may want to change the check for $use_default_dir
     *
     */
    
    /*
     $extension = substr( strrchr( $_POST['name'], '.' ), 1 );
     switch( $extension )
     {
        case 'jpg':
        case 'png':
        case 'gif':
            $customdir = '/images';
            break;
            
        case 'mp4':
        case 'm4v':
            $customdir = '/videos';
            break;
        
        case 'txt':
        case 'doc':
        case 'pdf':
            $customdir = '/documents';
            break;
     
        default:
            $customdir = '/others';
            break;
     }
    */
    
    $path['path']    = str_replace($path['subdir'], '', $path['path']); //remove default subdir (year/month)
    $path['url']     = str_replace($path['subdir'], '', $path['url']);      
    $path['subdir']  = $customdir;
    $path['path']   .= $customdir; 
    $path['url']    .= $customdir;  

    return $path;
}
3
  • This is epic! Thank you. Any chance you're listening to comments still? Just wondering clarification on the comment of Save uploads in FILETYPE based folders...you say you may want to change the check for $use_default_dir...why is that? And what would it change to?
    – codeview
    May 17, 2015 at 21:42
  • I tried this solution and found that this is not working when you are adding a new post and also uploading new images for that post. In that case, the images are uploading inside the 'uploads' folder. The code is working for the existing posts means when the post is already added and you are uploading new images on that post. Any solution for that?
    – Debashis
    Jun 25, 2022 at 18:49
  • @Debashis, I'm not seeing anything in my code that would handle this case. Meanwhile, you can instruct (or force) users to save the post first before uploading images...
    – brasofilo
    Jun 26, 2022 at 15:38
5

The plugin Custom Upload Dir has this capability:

[with] Custom Upload Dir [...] you can construct paths from variables like post title, category, post author, post date and many more.

::Screenshots::
Custom Upload Dir screenshot 1



Custom Upload Dir screenshot 2

0
0

For our internal webpage, we used the code from here and made it use the top level parent if the upload is from a page. Since it is internal, we didn't care about the username being visible. This allowed us to break up the uploads by something close to departments.

function wpse_25894_custom_upload_dir( $path ) {   
    /*
     * Determine if uploading from inside a page - if not, use the username
     */
    if ( ( strpos( $_SERVER['HTTP_REFERER'], 'wp-admin/upload' ) !== false ) || 
        ( strpos( $_SERVER['HTTP_REFERER'], 'wp-admin/media-new' ) !== false ) ) 
    {  
        $current_user = wp_get_current_user();    
        $customdir = '/' . $current_user->user_login;   
    } else {
        $post = get_post( $_REQUEST['post_id'] );    
        /* 
         * If there is a parent page, then get the hierarchy array, 
         * reverse it and use the first one to get the top level parent
         */
        if ( $post->post_parent ) {
            $parent = array_reverse( get_post_ancestors( $post->ID ) );
            $first_parent = get_page( $parent[0] );
            $customdir  = '/' . $first_parent->post_name;
        }
        /* 
         * Check if it is not a page (post or attachment page)
         * If it is, then use the username
         */
        elseif (strcmp ( 'page', $page->type ) == false ) {
            $current_user = wp_get_current_user();    
            $customdir = '/' . $current_user->user_login;
        }
        /*
         * Otherwise it is a top level parent page and just use the page name
         */
        else {
            $the_post = get_post( $_REQUEST['post_id'] );
            $customdir = '/' . $the_post->post_name;
        }
    }
    // Format the variables into a useable path
    $path['path']    = str_replace( $path['subdir'], '', $path['path'] ); // Remove default subdir (year/month)
    $path['url']     = str_replace( $path['subdir'], '', $path['url'] );      
    $path['subdir']  = $customdir;
    $path['path']   .= $customdir; 
    $path['url']    .= $customdir;  
    return $path;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.