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.

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.

2 Answers 2


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.

  • Spot on, thank you! I ended up creating a plugin that utilizes some of what you added above. I copied the old sites image folder structure into my uploads folder, traversed the old database, created a HUGE (75,000+) array of image paths, names and post parents, and ran the wp_insert_attachment as well as wp_update_post_meta, along with some other tips and tricks. I will post my final solution, thank you for the guidance. Commented Feb 8, 2013 at 15:04

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:


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:


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:

Plugin Name: Image Importer
Plugin URI: 
Author: xxxxx
Version: 1.0
Author URI: 


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/>';
            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>';


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

Your Answer

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

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