PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Sunday, September 18, 2022

[FIXED] When using PHP and ImageMagick to resize and name images recursively, how to best write new files based on the existing filename and extension

 September 18, 2022     imagemagick, imagick, php, regex     No comments   

Issue

I've got a directory of 'source' images that I'm saving out multiple resized files. I'd like all jpg and png files in a given directory to be resized and saved with new names based on their original file names (a la filename-small.jpg, filename-medium.jpg, etc.) I'm using ImageMagick.

The regex stuff to get all the files was found in another question here on SO and has helped—but I don't totally get what all is going on here.

What I have currently plops the original file extension in the middle of the file name. While I understand why this is happening, I'm not sure the best approach to fix this.

How can I get this to to be filename-large.jpg instead of filename.jpg-large.jpg?

Here is what I have currently:

<?php

    $directory = new RecursiveDirectoryIterator('source/assets/uploads/test');
    $iterator = new RecursiveIteratorIterator($directory);
    $images = new RegexIterator($iterator, '/^.+(.jpe?g|.png)$/i', RecursiveRegexIterator::GET_MATCH);

    $widths = [
        'small' => 400,
        'medium' => 800,
        'large' => 1000,
        'xlarge' => 1600,
    ];
    
    foreach($images as $image => $value) {

        // Set the original filename w/o the extension
        $originalFilename = $image;

        // set the extension based on the original
        // ideally conform the extension jpeg to jpg
        $originalExtension = '.jpg';
        
        // create a new Imagick instance
        $newImage = new Imagick($image);

        // strip out some junk
        $newImage->stripImage();

        // write a new file for each width 
        // and name it based on the original filename
        foreach ($widths as $key => $value) {
            // using 0 in the second $arg will keep the same image ratio
            $newImage->resizeImage($value,0, imagick::FILTER_LANCZOS, 0.9);
            $newImage->writeImage($originalFilename . '-' . $key . $originalExtension);
        }
    }

Solution

Nathan,

You are capturing that extension with the parens. You want to capture the file name, not just the extension. You can use non-caputring parens ((?:)) for grouping the extension variations.

$images = new RegexIterator($iterator, '/^(.+)\.(?:jpe?g|png)$/i', RecursiveRegexIterator::GET_MATCH);

I also escaped the literal dots (.) before the extensions.

Alternatively, you could just split up the extension and filename from the $image variable:

preg_match('/^(.+)\.([^.]+)$/', $image, $matches);
// Set the original filename w/o the extension
$originalFilename = $matches[1];
// Set the original extension
$originalExtension = $matches[2];

Lastly, FWIW, were I to do this I would not use the RegexIterator but tather just iterate the directory and just use preg_match only on each filename.

$directory = dir('source/assets/uploads/test');
while(false !== ($entry = $directory->read())) {
    $result = preg_match('/^(.+)\.(jpe?g|png)$/i', $entry, $matches);
    if($result === false) {
      die('ERROR: `preg_match` failed on '.$entry);
    } else if($result === 0) {
      // not an image file
      continue;
    }
    // Set the original filename w/o the extension
    $originalFilename = $matches[1];
    // Set the original extension
    $originalExtension = $matches[2];

    // ... do the other things on the file
}

Refs:

  • https://www.php.net/manual/en/function.dir
  • https://www.php.net/manual/en/function.preg-match.php

EDIT:

For named references inside of $matches you can use this modified regexp:

preg_match('/^(?P<filename>.+)\.(?P<extension>jpe?g|png)$/i', $entry, $matches);

The ?P<key> is a "placeholder" that assigns key as a reference to that caputred match.

You are still going to get $matches back as an array, not an object, but then you can access the values with that placeholder as the index.

// Set the original filename w/o the extension
$originalFilename = $matches['filename'];
// Set the original extension
$originalExtension = $matches['extension'];

FWIW, the numeric indexes are still there too.



Answered By - Karl Wilbur
Answer Checked By - Dawn Plyler (PHPFixing Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing