Importing WordPress Attachments Into Custom Directories In wp-content/uploads/

I am hoping that someone can shed light on this problem I am trying to solve, even if simply changing my thought process as to how I could solve this.

I am importing a custom CakePHP based CMS web application into WordPress by means of traversing the database, making numerous SQL queries, building arrays and finally outputting the necessary XML file for import using the WordPress import tool.

Read More

Currently I have managed to import categories, tags, and posts / pages just fine, no hiccups, however it is the images that seem to be the issue. The current format of images in the CakePHP based CMS is images/year/month/day/post_id/image_name.ext

As far as I understand, but default WordPress can only store media inside wp-content/uploads or wp-content/uploads/year/month

What I have done is copied over all the image folders from the CakePPH app directory into my WordPress wp-content/uploads directory, so an example image will have this path: http://localhost/wordpress_site/wp-content/uploads/2013/01/01/12345/image.jpg

Inside my WordPress XML file that I am generating, I have set the attachment_url as well as GIUD to be as it is above, referencing the new image I place inside my uploads directory. I even set the file path in the serialzied meta data of the attachment element in the XML.

If I run the import process and DO NOT check the ‘Download and import attachments’ option in WordPress, I get errors that the media cannot be imported…

If I DO check the ‘Download and import attachments’ option, the images ARE located inside the custom path I specified above, but they are resized according to the thumbnail dimensions etc, and the image is still put inside a year/month/image.jpg path. So all WordPress did was read the file at that (custom) location, but still set the final path to a standard WordPress one, moving the image and its generated thumbnails over and away form the desired location.

Does anyone know of a way to simply have the media in a custom path inside the uploads folder AND force WordPress to accept and use that path when running an import?

I understand that via actions and hooks I can change the upload path structure, but only within WordPress and only when uploading media via the post / page / media manager, not during an import. The last thing I want to do is hack the import script.

Any help, guidance, words of advice will be more than appreciated.

Related posts

Leave a Reply

2 comments

  1. Yes you can by programatically adding the attachment.

    $upl = wp_upload_dir();
    $target = $upl['basedir'] . '/YOUR_CUSTOM_FOLDERS/YOUR_FILE';
    $filetype = wp_check_filetype($target);
    
    $attachment = array(
        'post_mime_type' => $filetype['type'],
        'post_title' =>  $YOUR_FILE_TITLE,
        'post_content' => '',
        'post_status' => 'inherit'
    );
    
    wp_insert_attachment( $attachment, $target, $POST_ID_TO_ATTACH_TO );
    

    This will tell WordPress that there is an attachment at $target and you can optionally attach it to a post with the last parameter in the call to wp_insert_attachment.

  2. To everyone who faces the same dilemma as I did, there are actually a lot of online resources dictating how this issue can be solved. I found the best way to find the solution was actually to search for something like Import WordPress attachments programatically, which churned up a fair amount of solutions.

    Thank you to @tobbr for his valuable guidance in pointing me in the correct direction. My final solution involved creating a plugin, and then traversing the old sites (CakePHP) database for old images and their paths, then using those to import the attachments into WordPress.

    The important key point to take into consideration here is that I CLONED the old sites images directory into my WordPress uploads directory. The old image paths were as follows:

    CAKE_ROOT/app/WebRoot/images/2012/10/2/12345/image_name.jpg

    Where 12345 = the ID of the image entry in the database (which would become the ID of the attachment when importing into WordPress. So I moved the ENTIRE images folder above into my WordPress uploads directory, so my WordPress image folder structure looked like follows:

    WP_ROOT/wp-content/uploads/2012/10/2/12345/image_name.jpg

    It is important to note that before you even begin to import attachments programatically, your images MUST live somewhere (anywhere) within your WordPress uploads directory. Also make sure you have the necessary permissions, for the sake of the import I ran a chmod -R 777 command on my uploads folder before starting.

    I then wrote the plugin, which was not nearly as complicated as I imagined, so here it is for anyone needing to do a similar task:

    <?php
    /*
    Plugin Name: Image Importer
    Plugin URI: 
    Description:
    Author: xxxxx
    Version: 1.0
    Author URI: 
    */
    
    error_reporting(E_ALL);
    
    add_action( 'admin_menu', 'sd_plugin_menu' );
    
    function sd_plugin_menu() {
        add_options_page( 'My Plugin Options', 'My Image Importer', 'manage_options', 'sd-image-importer', 'sd_image_importer' );
    }
    
    function sd_image_importer() {
        if ( !current_user_can( 'manage_options' ) )  {
            wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
        }
    
        echo '<div style="overflow:hidden;padding:20px;margin:20px 0 0 0;border:1px solid #ccc;font-size:14px;color:#666;">';
    
        if($_REQUEST['getfiles'] == 1) {
    
        // Make a MySQL Connection
        $con = mysqli_connect("xxx", "xxx", "xxx") or die(mysql_error());
        mysqli_select_db($con,"xxxx") or die(mysqli_error($con));
    
        // Retrieve all the data from the images table
        $result = mysqli_query($con,"SELECT * FROM cake_attached_images WHERE created > '2012-10-01' AND created < '2012-10-02'") or die(mysql_error($con));  
    
        // pre buffer empty array so as not to create one on each iteration of DB
        $files = array();
    
            while ($row = mysqli_fetch_array($result,MYSQL_ASSOC)) {
                $year = date('Y', strtotime($row['created']));
                $month = date('m', strtotime($row['created']));
                $day = ltrim(date('d', strtotime($row['created'])),0);
                $id = $row['id'];
                $foreign_key = $row['foreign_key'];
                $caption = $row['caption'];
                $filename = $row['filename'];
    
                $files[$id] = array(
                    'id' => $id,
                    'foreign_key' => $foreign_key,
                    'path' => "$year/$month/$day/$id/$filename",
                );
            };
        }; // End if getfiles param present in URL
    
        if($_REQUEST['insert'] == 1){
    
            global $wpdb; 
    
            foreach($files as $file){
    
              $wp_filetype = wp_check_filetype(basename($wp_upload_dir['baseurl'].'/'.$file['path']), null );
              $wp_upload_dir = wp_upload_dir();
    
              $attachment = array(
                 'guid' => $wp_upload_dir['baseurl'].'/'. $file['path'], 
                 'post_mime_type' => $wp_filetype['type'],
                 'post_title' => preg_replace('/.[^.]+$/', '', basename($wp_upload_dir['baseurl'].'/'.$file['path'])),
                 'post_content' => '',
                 'post_status' => 'inherit',
                 'post_parent' => $file['foreign_key']
              );
    
              // Insert attachment, newly added attachmnet ID is returned...
              $attach_id = wp_insert_attachment( $attachment, $wp_upload_dir['path'].'/'. $file['path'], $file['foreign_key'] );
              // The file MUST be included to make use of the wp_generate_attachment_metadata function below
              require_once(ABSPATH . 'wp-admin/includes/image.php');
              // Generate metadata from newly added attachment above
              $attach_data = wp_generate_attachment_metadata( $attach_id, $wp_upload_dir['path'].'/'. $file['path'] );
              // Updates atttachment metadata for newly added attachmnet based on the generated metadata above
              if(wp_update_attachment_metadata( $attach_id, $attach_data )){
                // Optional, can be sued to set the added attachmnet as the featured image on the post
                add_post_meta($file['foreign_key'], '_thumbnail_id', $attach_id, true);
                // Simple outout to indicate if attachmnet was added or not, for debugging
                echo '<span style="color:green;">Attachment successfully added for post with ID <strong>'.$file['foreign_key'].'</strong>, path of new attachment is at <strong>'.$wp_upload_dir['baseurl'].'/'.$file['path'].'</strong></span><br/>';
              }else{
                echo '<span style="color:red;">Failed to generate new attachment for post with ID <strong>'.$file['foreign_key'].'</strong></span><br/>';
              }
    
            }
        } // end if insert param present in URL request
    
        $files = null;
    
        echo '</div>';
    }?>
    

    AND HERE IS A BONUS FUNCTION:

    I came across this AFTER writing the plugin, however if you only have a few (I had over 100,000 images to import!) there is a VERY handy function in WordPress called media_sideload_image which grabs an image from a remote URL and attaches it to a post of your choice, pretty worth taking a look at. Get more info at the codex: http://codex.wordpress.org/Function_Reference/media_sideload_image


    Some links to WordPress Codex resources that helped me are as follows:

    1. http://codex.wordpress.org/Function_Reference/wp_insert_attachment
      Inserting attachments programatically
    2. http://codex.wordpress.org/Function_Reference/wp_update_attachment_metadata – Updating meta data for newly added attachment add_post_meta – If you wish to set the newly added image as a featured image

    Other external sites that aided me:

    1. http://www.zdnet.com/blog/diy-it/programmatically-importing-thousands-of-featured-image-post-thumbnails-into-wordpress/118 – ZDNet
    2. http://nlb-creations.com/2012/09/26/how-to-programmatically-import-media-files-to-wordpress/ – NB Creations
    3. Programmatically get images by URL and save in uploads folder – WordPress Answers
    4. Programatically creating image attachments from local URLs and setting featured image – WordPress answers

    I hope this helps someone as it did me.
    Simon