Fixing broken images when Drupal adds _0 to uploaded file names; filename and filepath differs

druplicon.large_.pngOccasionally on Drupal sites (perhaps only Drupal 6), uploading a file to a file field, or the file uploads area on a node, results in the "filepath" indicating a different name than the "filename". That is, in the data model for a Node, you can attach files in the "uploads" or in a CCK field (in Drupal 7 this is slightly different) and each attached file is an object (or array) containing attributes named "filename" and "filepath". Normally the last component of filepath is the same as filename, but these can have different values.

For example, if there's already a file uploaded having the same name, Drupal will modify the file name so as to not overwrite the existing file. That's a good thing.

On the site I manage, we've seen filename and filepath differ even when there is no existing file by the same name. We haven't bothered to figure out why this happens, because we have a good workaround.

The effect of this is that upon referring to that file (say - if it's an image, so using it in an IMG tag) using the filename attribute, then most of the time the code will successfully refer to the file (render a correct IMG tag) but sometimes, when filename and filepath differ, it will fail. Scratch head. Scream.

Here's an example of what the data structure looks like:

   'field_homepage_image' => array(
      '0' => array(
        'fid' => '11320',
        'uid' => '4070',
        'filename' => 'i3-feature.png',
        'filepath' => 'sites/default/files/i3-feature_0.png',
        'filemime' => 'image/png',
        'filesize' => '14434',
        'status' => '1',
        'timestamp' => '1362005225',
        'list' => '1',
        'data' => array(
          'alt' => '',
          'title' => '',
        ),
      ),
    ),

The uploaded file, named 'i3-feature.png', became 'sites/default/files/i3-feature_0.png'.

In a theme file you might do something like

<?php
if ($node->field_homepage_image && $node->field_homepage_image[0]) {
      
$img = $node->field_homepage_image[0];
      
$imgFN = $img['filename'];
|
...
?>

...
<?php print theme('imagecache', 'thumb_150w', $imgFN); ?>

This uses Image Cache to render a scaled version of the image, but with the data above it will fail because the file named in filename is not the one we want.

This strikes me as a bug. If Drupal changes filepath it should also change filename to match. In the issues I found on Drupal.org, however, the people dealing with the bug report didn't see this as a Drupal bug. Maybe I'm missing something?

In any case here's an excellent workaround.

<?php
if ($node->field_homepage_image && $node->field_homepage_image[0]) {
      
$img = $node->field_homepage_image[0];
      
// $imgFN = $img['filename'];
      
if (strpos($img['filepath'], "sites/default/files/") == 0) {
         
$imgFN = substr_replace($img['filepath'], "", 0, 20);
       } else if (
strpos($img['filepath'], "files/") == 0) {
         
$imgFN = substr_replace($img['filepath'], "", 0, 6);
       } else {
         
$imgFN = $img['filename'];
       }
}
...
?>

...
<?php print theme('imagecache', 'thumb_150w', $imgFN); ?>

You should adjust this based on the layout of your site. What it's doing is accommodating for whether the file is uploaded to sites/default/files or to files (as is done for some sites). If filepath begins with either, that part is stripped off the string, and the result is used as the filename. Otherwise the filename attribute is used.

In most cases the file will be uploaded to sites/default/files (NOTE: adjust this if you're using a domain name). To take the above example, sites/default/files/i3-feature_0.png becomes i3-feature_0.png which is exactly what we want, and is exactly what works for theming with Image Cache.