Upload and resize images with Zend_Form_Element_File

The Zend Framework includes a class called Zend_Form_Element_File for creating a file upload within a form. It uses Zend_File_Transfer for receiving the file, but the whole process lacks some methods for the often required image upload.

What is easily possible

Let’s begin with the things that are possible. You can specify several validators for your file to check for file extensions or maximum file size. That’s stuff which is required for all files and therefore it is included. You may also specify a target directory.

// part of my form class (Default_Form_Photo::init)
$photo = new Zend_Form_Element_File('photo');
$photo->setLabel('Photo')
      ->setDestination(Zend_Registry::get('config')->paths->backend->images->profile);
// ensure only one file
$photo->addValidator('Count', false, 1);
// max 2MB
$photo->addValidator('Size', false, 2097152)
      ->setMaxFileSize(2097152);
// only JPEG, PNG, or GIF
$photo->addValidator('Extension', false, 'jpg,png,gif');
$photo->setValueDisabled(true);
$this->addElement($photo, 'photo');

Renaming a file according to your needs

Renaming a file according to your needs is also possible, even though (often) not as easily as the other stuff. You need to add a filter after initialising, because you usually do not know the filename at runtime. At least when you upload a profile picture you often want to give it a name like the username or the user’s id.

Therefore, you have to add the Rename-filter in your controller when a file is uploaded

// part of my controller after an upload (Default_UserController)
if ($photo->getElement('photo')->isUploaded()) {
    $extension = pathinfo($photo->getElement('photo')->getValue(), PATHINFO_EXTENSION); 
 
    $photo->getElement('photo')->addFilter('Rename', array(
        'target' => $user->getUsername() . '.' . $extension,
        'overwrite' => true
    ));
 
    if ($photo->getElement('photo')->receive()) {
        $profile = $user->getProfile();
        $profile->setPicture($photo->getElement('photo')->getValue());
        $profile->save();
    }
}

This filter will rename the upload (only one file is allowed) according to the rule in target.

Resizing an image

The difficult part with Zend is resizing the image. Of course you can do this in your controller after you received the upload, but this is not very nice style. As Zend supports filters, we better program a new filter for this task. I called it Skoch_Filter_File_Resize:

<?php
// Skoch/Filter/File/Resize.php
/**
 * Zend Framework addition by skoch
 * 
 * @category   Skoch
 * @package    Skoch_Filter
 * @license    http://opensource.org/licenses/gpl-license.php GNU Public License
 * @author     Stefan Koch <cct@stefan-koch.name>
 */
 
/**
 * @see Zend_Filter_Interface
 */
require_once 'Zend/Filter/Interface.php';
 
/**
 * Resizes a given file and saves the created file
 *
 * @category   Skoch
 * @package    Skoch_Filter
 */
class Skoch_Filter_File_Resize implements Zend_Filter_Interface
{
    protected $_width = null;
    protected $_height = null;
    protected $_keepRatio = true;
    protected $_keepSmaller = true;
    protected $_directory = null;
    protected $_adapter = 'Skoch_Filter_File_Resize_Adapter_Gd';
 
    /**
     * Create a new resize filter with the given options
     *
     * @param Zend_Config|array $options Some options. You may specify: width, 
     * height, keepRatio, keepSmaller (do not resize image if it is smaller than
     * expected), directory (save thumbnail to another directory),
     * adapter (the name or an instance of the desired adapter)
     * @return Skoch_Filter_File_Resize An instance of this filter
     */
    public function __construct($options = array())
    {
        if ($options instanceof Zend_Config) {
            $options = $options->toArray();
        } elseif (!is_array($options)) {
            require_once 'Zend/Filter/Exception.php';
            throw new Zend_Filter_Exception('Invalid options argument provided to filter');
        }
 
        if (!isset($options['width']) && !isset($options['height'])) {
            require_once 'Zend/Filter/Exception.php';
            throw new Zend_Filter_Exception('At least one of width or height must be defined');
        }
 
        if (isset($options['width'])) {
            $this->_width = $options['width'];
        }
        if (isset($options['height'])) {
            $this->_height = $options['height'];
        }
        if (isset($options['keepRatio'])) {
            $this->_keepRatio = $options['keepRatio'];
        }
        if (isset($options['keepSmaller'])) {
            $this->_keepSmaller = $options['keepSmaller'];
        }
        if (isset($options['directory'])) {
            $this->_directory = $options['directory'];
        }
        if (isset($options['adapter'])) {
            if ($options['adapter'] instanceof Skoch_Filter_File_Resize_Adapter_Abstract) {
                $this->_adapter = $options['adapter'];
            } else {
                $name = $options['adapter'];
                if (substr($name, 0, 33) != 'Skoch_Filter_File_Resize_Adapter_') {
                    $name = 'Skoch_Filter_File_Resize_Adapter_' . ucfirst(strtolower($name));
                }
                $this->_adapter = $name;
            }
        }
 
        $this->_prepareAdapter();
    }
 
    /**
     * Instantiate the adapter if it is not already an instance
     *
     * @return void
     */
    protected function _prepareAdapter()
    {
        if ($this->_adapter instanceof Skoch_Filter_File_Resize_Adapter_Abstract) {
            return;
        } else {
            $this->_adapter = new $this->_adapter();
        }
    }
 
    /**
     * Defined by Zend_Filter_Interface
     *
     * Resizes the file $value according to the defined settings
     *
     * @param  string $value Full path of file to change
     * @return string The filename which has been set, or false when there were errors
     */
    public function filter($value)
    {
        if ($this->_directory) {
            $target = $this->_directory . '/' . basename($value);
        } else {
            $target = $value;
        }
 
        return $this->_adapter->resize($this->_width, $this->_height,
            $this->_keepRatio, $value, $target, $this->_keepSmaller);
    }
}

Adapter classes

As you might see this file also requires an adapter to ensure you can use the filter with both GD and Imagick. Thus, we need an abstract class and the implementation classes:

<?php
// Skoch/Filter/File/Resize/Adapter/Abstract.php
/**
 * Zend Framework addition by skoch
 * 
 * @category   Skoch
 * @package    Skoch_Filter
 * @license    http://opensource.org/licenses/gpl-license.php GNU Public License
 * @author     Stefan Koch <cct@stefan-koch.name>
 */
 
 
/**
 * Resizes a given file and saves the created file
 *
 * @category   Skoch
 * @package    Skoch_Filter
 */
abstract class Skoch_Filter_File_Resize_Adapter_Abstract
{
    abstract public function resize($width, $height, $keepRatio, $file, $target, $keepSmaller = true);
 
    protected function _calculateWidth($oldWidth, $oldHeight, $width, $height)
    {
        // now we need the resize factor
        // use the bigger one of both and apply them on both
        $factor = max(($oldWidth/$width), ($oldHeight/$height));
        return array($oldWidth/$factor, $oldHeight/$factor);
    }
}

gd implementation

<?php
// Skoch/Filter/File/Resize/Adapter/Gd.php
/**
 * Zend Framework addition by skoch
 * 
 * @category   Skoch
 * @package    Skoch_Filter
 * @license    http://opensource.org/licenses/gpl-license.php GNU Public License
 * @author     Stefan Koch <cct@stefan-koch.name>
 */
 
require_once 'Skoch/Filter/File/Resize/Adapter/Abstract.php';
 
/**
 * Resizes a given file with the gd adapter and saves the created file
 *
 * @category   Skoch
 * @package    Skoch_Filter
 */
class Skoch_Filter_File_Resize_Adapter_Gd extends
    Skoch_Filter_File_Resize_Adapter_Abstract
{
    public function resize($width, $height, $keepRatio, $file, $target, $keepSmaller = true)
    {
        list($oldWidth, $oldHeight, $type) = getimagesize($file);
 
        switch ($type) {
            case IMAGETYPE_PNG:
                $source = imagecreatefrompng($file);
                break;
            case IMAGETYPE_JPEG:
                $source = imagecreatefromjpeg($file);
                break;
            case IMAGETYPE_GIF:
                $source = imagecreatefromgif($file);
                break;
        }
 
        if (!$keepSmaller || $oldWidth > $width || $oldHeight > $height) {
            if ($keepRatio) {
                list($width, $height) = $this->_calculateWidth($oldWidth, $oldHeight, $width, $height);
            }
        } else {
            $width = $oldWidth;
            $height = $oldHeight;
        }
 
        $thumb = imagecreatetruecolor($width, $height);
 
        imagealphablending($thumb, false);
        imagesavealpha($thumb, true);
 
        imagecopyresampled($thumb, $source, 0, 0, 0, 0, $width, $height, $oldWidth, $oldHeight);
 
        switch ($type) {
            case IMAGETYPE_PNG:
                imagepng($thumb, $target);
                break;
            case IMAGETYPE_JPEG:
                imagejpeg($thumb, $target);
                break;
            case IMAGETYPE_GIF:
                imagegif($thumb, $target);
                break;
        }
        return $target;
    }
}

Using the filter

This filter can now be attached to your Zend_Form_Element_File instance and will then resize the image to produce a thumbnail:

$photo->addFilter(new Skoch_Filter_File_Resize(array(
    'width' => 200,
    'height' => 300,
    'keepRatio' => true,
)));

You may specify several options invoking the filter. As you see in my code, I used with, height and keepRatio resulting in two maximum sizes. The image will then be resized so that it fits both of the lengths, but the aspect ratio will be kept. The whole list of options:

  • width: The maximum width of the resized image
  • height: The maximum height of the resized image
  • keepRatio: Keep the aspect ratio and do not resize to both width and height (usually expected)
  • keepSmaller: Do not resize if the image is already smaller than the given sizes
  • directory: Set a directory to store the thumbnail in. If nothing is given, the normal image will be overwritten. This will usually be used when you produce thumbnails in different sizes.
  • adapter: The adapter to use for resizing. You may specify a string or an instance of an adapter.

Now it’s easily possible to resize an uploaded image. To automatically load the classes, you need to add an option to your application.ini.

autoloaderNamespaces[] = "Skoch_"

Multiple thumbnails

Often you want to create several thumbnails in different sizes. This can be done by using a so called filter chain and the directory option of the Skoch_Filter_File_Resize.

If you specify directory, the value of setDestination() will not be considered anymore. Thus, you have to pass the full path to the directory option.

$filterChain = new Zend_Filter();
// Create one big image with at most 600x300 pixel
$filterChain->appendFilter(new Skoch_Filter_File_Resize(array(
    'width' => 600,
    'height' => 300,
    'keepRatio' => true,
)));
// Create a medium image with at most 500x200 pixels
$filterChain->appendFilter(new Skoch_Filter_File_Resize(array(
    'directory' => '/var/www/skoch/upload/medium',
    'width' => 500,
    'height' => 200,
    'keepRatio' => true,
)));
// Rename the file, of course this should not be a fixed string in real applications
$multiResize->addFilter('Rename', 'users_upload');
// Add the filter chain with both resize rules
$multiResize->addFilter($filterChain);

Download

You can download the library and a tiny example from my github repository.

Caveats

If you want to use the directory option together with renaming, make sure to add the Resize-filter after the Rename-filter to ensure that Resize gets the new filename and will save the thumbnail with the new filename. Otherwise you might get this structure:

/img/gallery/stefan/thumbs/Spain_1000.png
/img/gallery/stefan/1234.png

Where you probably do not want to have the filename Spain_1000.png on your server ;) So don’t forget to add Resize after Rename.

38 Gedanken zu “Upload and resize images with Zend_Form_Element_File

  1. First of all thanks for this filter, I was on the way to write mine when i found yours :).
    i’ve added few lines to be able to manage transparency for PNG & GIF:

    // instead of (old) :
    // $thumb = imagecreatetruecolor($width, $height);
    //
    // imagecopyresized($thumb, $source, 0, 0, 0, 0, $width, $height, $oldWidth, $oldHeight);
    // Put :
    $thumb = imagecreatetruecolor($width, $height);
    // Manage alpha 
    imagealphablending($thumb,false);
    imagesavealpha($thumb,true);
     // Resample instead of resize to keep alpha channel
    imagecopyresampled($thumb, $source, 0, 0, 0, 0, $width, $height, $oldWidth, $oldHeight);

    Again thx for you’re work, hope this addon will help you or others.
    Dede

  2. Awesome :) If you have any other additions, feel free to add them.

    I also found a new caveat, you need to add the rename filter before my resize-filter if you use the directory-option, otherwise the file will be stored according to the name on the user’s computer.

    I added your lines to my code above.

  3. Great Work, thank you very much for sharing this with us. I just got an urgent question, I am trying to re-size the image 3 times. I learnt that when you add a directory value, you get the image in 2 sizes, but one size is the original, which would than be out of my control.

    Is there a way to re-size multiple times with your filter? I can’t figure it out, but have the feeling it is not possible. Still great work ! Luka

    • Hi Luka, you are asking at the right time as I had the same problem in a current project. You can create multiple sizes at the same time with a filter chain:

      $filterChain = new Zend_Filter();
      // Create one big image with at most 600x300 pixel
      $filterChain->appendFilter(new Skoch_Filter_File_Resize(array(
      	'width' => 600,
      	'height' => 300,
      	'keepRatio' => true,
      )));
      // Create a medium image with at most 500x200 pixels
      $filterChain->appendFilter(new Skoch_Filter_File_Resize(array(
      	'directory' => Zend_Registry::get('config')->backend->images->article->medium->path,
      	'width' => 500,
      	'height' => 200,
      	'keepRatio' => true,
      )));
      // Create a small image with at most 300x100 pixels
      $filterChain->appendFilter(new Skoch_Filter_File_Resize(array(
      	'directory' => Zend_Registry::get('config')->backend->images->article->thumb->path,
      	'width' => 300,
      	'height' => 100,
      	'keepRatio' => true,
      )));
      // Add the rename filter
      $uploadPhotoForm->getElement('photo')->addFilter('Rename', array(
      	'target' => $article->getUrl() . '.' . $extension,
      	'overwrite' => true
      ));
      // Add the resize filter chain
      $uploadPhotoForm->getElement('photo')->addFilter($filterChain);

      If you have any problems, just ask again. You have to use a filter chain, because Zend_File_Transfer does not allow you to add a filter from the same class twice (because it stores them in an array with class-name as index).

  4. Cool, that works great. Thank you so much !! Might be a rather silly question, but would you know if it’s also possible to give them different names? My script was already set up to work with different names for the different sizes and it would be great, if I don’t have to rewite all the code… Thank you again.

    Luka

    • Hi Luka, this is just an idea, I have not tried it. Maybe you can create sort of groups within the filter chain by creating 3 filter chains which contain each one Zend_Filter_File_Rename and one Skoch_Filter_File_Resize. Then the rename filter could rename it before the resize filter would store it. When the parser leaves the chain, everything would fall back and in the next sub-chain another rename-filter would rename it.

      In Pseudocode it could look like this:
      chain1 = new Zend_Flter();
      chain1->add(new Zend_Filter_File_Rename(name1));
      chain1->add(new Skoch_Filter_File_Resize());
      chain2 = new Zend_Flter();
      chain2->add(new Zend_Filter_File_Rename(name2));
      chain2->add(new Skoch_Filter_File_Resize());
      chain3 = new Zend_Flter();
      chain3->add(new Zend_Filter_File_Rename(name3));
      chain3->add(new Skoch_Filter_File_Resize());

      chain = new Zend_Filter();
      chain->add(chain1);
      chain->add(chain2);
      chain->add(chain3);

      photoUpload->addFilter(chain);

      If that does not work, you can always rewrite the filter class to allow a new parameter called ‘filename’. I just did not add it, because there is the Rename filter.

      You would have to add in the construct of Skoch_Filter_File_Resize:

      if (isset($options['filename'])) {
         $this->_filename = $options['filename'];
      }

      And then in filter:

      if ($this->_directory) {
          if ($this->_filename) {
              $target = $this->_directory . '/' . $this->_filename;
          } else {
              $target = $this->_directory . '/' . basename($value);
          }
      } else {
          if ($this->_filename) {
              $target = dirname($target) . '/' . $this->_filename;
          } else {
              $target = $value;
          }
      }

      Hope this helps.

      • Actually, the line:

        $target = dirname($target) . ‘/’ . $this->_filename;

        should be

        $target = dirname($value) . ‘/’ . $this->_filename;

        By the way, very nice work, man. Thanks a lot!!!

  5. Great, I got it to work. I have choosen your second solution, because I could not get the first one to work.

    I only had to do a small change

    from: $target = dirname($target) . ‘/’ . $this->_filename;
    to: $target = $this->_filename;

    Now it all works. Thank you so much, I was looking for ages to find a resize Function. Great Work !

  6. Hello stefan
    Thank you for this codes. I use your codes and I have little problem whit this. when I set resize images in 3 or 4 size and I use your filterChain codes, if image width or height is smaller than my set size resize don’t work and retun arrey if width and height is bigger it work whit out problem.

    And do you have any idea about get Image Dimensions and size whit this filter?

    • I just tried to reproduce it, but here it works fine. I used an image of 100×56 and the target values were 600×300 and 500×200. I tried it with both keepSmaller = true and keepSmaller = false and it worked in both cases as expected.

      Have you found some solution for your problem or can you provide further information?

  7. Hello!
    How can a filter be used twice in one image?
    The code below executes only the second filter…
    $file = new Zend_Form_Element_File(‘file’);
    $file->setLabel(‘Upload File’)
    ->addFilter(new Square_Filter_File_Resize(array(
    ‘width’ => 200,
    ‘height’ => 300,
    ‘keepRatio’ => true,
    ‘directory’ => APPLICATION_PATH . ‘/../public/uploads’,
    )))
    ->addFilter(new Square_Filter_File_Resize(array(
    ‘width’ => 20,
    ‘height’ => 30,
    ‘keepRatio’ => true,
    ‘directory’ => APPLICATION_PATH . ‘/../public/uploads/thumbs’,
    )))
    ->setRequired(true);

    Thanx!

  8. This work only one image.
    How do that work in more of the one image?
    $adapter = new Zend_File_Transfer_Adapter_Http();
    $adapter->addFilter(new Zend_Filter_File_Resize(array(
    ‘width’ => 440,
    ‘height’ => 300,
    ‘keepRatio’ => false,
    )));
    $adapter->addFilter(new Zend_Filter_File_Resize(array(
    ‘directory’ => $thumb,
    ‘width’ =>100,
    ‘height’ => 100,
    ‘keepRatio’ => false,
    )));
    Don’t work replace the second image with thumb :(

      • Hi Marcio,

        I use a filter chain.

        $filterChain = new Zend_Filter();
        $filterChain->appendFilter(new Skoch_Filter_File_Resize(array(
            'width' => Zend_Registry::get('config')->backend->images->profile->width,
            'height' => Zend_Registry::get('config')->backend->images->profile->height,
            'keepRatio' => true,
        )));
        $filterChain->appendFilter(new Skoch_Filter_File_Resize(array(
            'directory' => Zend_Registry::get('config')->backend->images->profile->medium->path,
            'width' => Zend_Registry::get('config')->backend->images->profile->medium->width,
            'height' => Zend_Registry::get('config')->backend->images->profile->medium->height,
            'keepRatio' => true,
        )));
        $filterChain->appendFilter(new Skoch_Filter_File_Resize(array(
            'directory' => Zend_Registry::get('config')->backend->images->profile->thumbnail->path,
            'width' => Zend_Registry::get('config')->backend->images->profile->thumbnail->width,
            'height' => Zend_Registry::get('config')->backend->images->profile->thumbnail->height,
            'keepRatio' => true,
        )));
        $filterChain->appendFilter(new Skoch_Filter_File_Resize(array(
            'directory' => Zend_Registry::get('config')->backend->images->profile->mini->path,
            'width' => Zend_Registry::get('config')->backend->images->profile->mini->width,
            'height' => Zend_Registry::get('config')->backend->images->profile->mini->height,
            'keepRatio' => true,
        )));
         
        $this->getElement('photo')->addFilter('Rename', array(
            'target' => $user->getUsername() . '.' . $extension,
            'overwrite' => true
        ));
         
        $this->getElement('photo')->addFilter($filterChain);
      • Glad you found the solution. I did not see your question, because I do not have my blog e-mail in my netbook’s Thunderbird.

        From reading your solution, it seems I should really refactor my Skoch_Resize soon. Seems quite buggy and that task has been on my wishlist for so long :/ And now the system becomes more and more popular, so it should not be buggy…

        • Hello to all.
          I have a problem. I am not able to operate the filter.
          I added in library the directory Skoch, I added the option in application.ini.
          I send the photo, is renamed and saved in the directory, but not resized.
          I guess I do not call the method receive properly or have to do an extra step. But where?

          I have:

          class Site_Form_MyElement_Photo extends Zend_Form_Element_File
          {
          public function __construct($nome)
          {
          parent::__construct($nome);

          $this->setLabel(‘Load photo:’)
          $this->addFilter(new Skoch_Filter_File_Resize(array(
          ‘width’ => 200,
          ‘height’ => 300,
          ‘keepRatio’ => true,
          )));

          }
          }

          Then

          class Site_Form_Photo extends Zend_Form
          {
          function __construct()
          {
          $argv = func_get_args();
          switch( func_num_args() )
          {
          case 0:
          self::__construct1(NULL);
          break;
          case 1:
          self::__construct1($argv[0]);
          break;
          }
          }

          public function __construct1($id_a)
          {
          parent::__construct();

          $this->setAction(‘/album/photo/new’)
          ->setMethod(‘post’);

          $name = new Site_Form_MyElement_NameFile(‘name_photo’);
          $name->setLabel(‘Name Photo:’);

          $image = new Site_Form_MyElement_Photo(‘image’);

          $id_album = new Site_Form_MyElement_HiddenId(‘id_album’);

          if(is_numeric($id_a))
          $id_album->setValue($id_a);

          $submit = new Zend_Form_Element_Submit(‘submit’);
          $submit->setLabel(‘Ok’);

          // Aggiunge gli elementi al form per comporlo
          $this->addElement($name)
          ->addElement($image)
          ->addElement($id_album)
          ->addElement($submit);
          }
          }

          The controller

          class PhotoController extends Zend_Controller_Action
          {
          public function preDispatch()
          {
          $filters = array(
          ‘numberAlbum’ => array(‘HtmlEntities’, ‘StripTags’, ‘StringTrim’)
          );
          $validators = array(
          ‘numberAlbum’ => array(‘NotEmpty’, ‘Int’)
          );

          $input = new Zend_Filter_Input($filters, $validators);
          $input->setData($this->getRequest()->getParams());

          $model_album = new Model_Album();
          if(isset($input->numeroAlbum))
          {
          $this->album = $model_album->getAlbum($input->numberAlbum);
          $this->id_album = $input->numberAlbum;
          }
          }

          public function newAction()
          {
          $form = new Sito_Form_Foto($this->id_album);
          $this->view->form = $form;

          if($this->getRequest()->isPost())
          {
          if($form->isValid($this->getRequest()->getPost()))
          {
          $model_album = new Model_Album();
          $album = $model_album->getAlbum($form->getValue(‘id_album’));

          if ($album instanceof Site_Class_Album)
          {
          $config = $this->getInvokeArg(‘bootstrap’)->getOption(‘uploads’);
          $form->immagine->setDestination($config[‘uploadPath’]);
          $adapter = $form->immagine->getTransferAdapter();

          if($adapter->getMimeType()!=NULL)
          {
          // Rename file
          $name_file = uniqid(md5(time()));
          $xt = @pathinfo($adapter->getFileName(‘image’), PATHINFO_EXTENSION);
          $adapter->clearFilters();
          $adapter->addFilter(‘Rename’, array(
          ‘target’ => sprintf(‘%s.%s’,$nome_file, $xt),
          ‘overwrite’ => true));

          if($adapter->receive(‘immagine’))
          {
          $name_file = $name_file.’.’.$xt;
          $date_insert = date(‘Y-m-d’, time());

          $photo = new Site_Class_Photo($form->getValue(‘name_photo’), $name_file,
          $date_insert, $album);

          $model_photo = new Model_Photo();
          if($model_foto->savePhoto($photo)==0)
          {
          $this->_helper->getHelper(‘FlashMessenger’)->addMessage(‘Foto ‘.$foto->getName().’ added.’);
          $this->_redirect(‘/album/photo/success’);
          }
          else
          {
          $this->_helper->getHelper(‘FlashMessenger’)->addMessage(“Error.”);
          $this->_redirect(‘/album/photo/error’);
          }
          }
          else
          {
          $this->_helper->getHelper(‘FlashMessenger’)->addMessage(“Error.”);
          $this->_redirect(‘/album/photo/error’);
          }
          }
          else
          {
          $this->_helper->getHelper(‘FlashMessenger’)->addMessage(“Error.”);
          $this->_redirect(‘/album/photo/error’);
          }
          }
          else
          {
          $this->_helper->getHelper(‘FlashMessenger’)->addMessage(“Error.”);
          $this->_redirect(‘/album/photo/error’);
          }
          }
          }

          }
          }

          Thank you in advance.

  9. Pingback: Zend Framework: Uploading and resizing an image | Saunders Web Solutions Blog

  10. Hello everyone!
    I want to first thank the developer of this code!
    This code is simple and easy to use, solves all the tasks assigned to it!
    But working with this code I noticed some features.
    Let me describe my problem.
    In my code, I had to get the data and photo files. I’m keeping the data in the database and the files are saved in a folder named result id.
    Pictures also have several sizes in separate directories and have different name. So I have to implement the code in action.
    I used this filter in action. But faced with problem – all images have the same small size!
    The solution was simple – if you first apply a filter with the small size, the remaining filters will also make small images!
    Therefore, we must first apply the filter with the largest size of the image and then with a little!

    Below is a sample code

    I would like to advise the developer of this wonderful code to the ability to crop images on the specified criteria.
    Sorry for my English.
    Best regards, Alymbek.

    ****************************************

    public function createAction()
    {
    $form = new Property_Form_AdminPropertyCreate;
    $this->view->form = $form;

    if ($this->getRequest()->isPost())
    {
    if ($form->isValid($this->getRequest()->getPost()))
    {
    $values = $form->getValues();
    $item = new Property_Model_Property;

    $item->date_created = date(‘Y-m-d’, mktime());
    $item->title = $values[‘title’];
    ………………….
    $item->status_id = $values[‘status_id’];
    $item->save();

    $id = $item->id;

    $filepath = PUBLIC_PATH . ‘/images/property/’ . $id;
    if (!mkdir($filepath, 0, true))
    {
    throw new Zend_Controller_Action_Exception(‘Failed to create folders…’);
    }

    $form->images->setDestination($filepath);
    $adapter = $form->images->getTransferAdapter();

    $filterImage = new Zend_Filter();

    $filepathBig = $filepath . ‘/big/';
    if (!mkdir($filepathBig, 0, true))
    {
    throw new Zend_Controller_Action_Exception(‘Failed to create folders for Big…’);
    }
    $filterImage->appendFilter(new Skoch_Filter_File_Resize(array(
    ‘directory’ => $filepathBig,
    ‘width’ => 720,
    ‘height’ => 480,
    ‘keepRatio’ => true,
    )));

    $filepathMiddle = $filepath . ‘/middle/';
    if (!mkdir($filepathMiddle, 0, true))
    {
    throw new Zend_Controller_Action_Exception(‘Failed to create folders for Middle…’);
    }
    $filterImage->appendFilter(new Skoch_Filter_File_Resize(array(
    ‘directory’ => $filepathMiddle,
    ‘width’ => 280,
    ‘height’ => 420,
    ‘keepRatio’ => true,
    )));

    $filepathSmall = $filepath . ‘/small/';
    if (!mkdir($filepathSmall, 0, true))
    {
    throw new Zend_Controller_Action_Exception(‘Failed to create folders for Small…’);
    }
    $filterImage->appendFilter(new Skoch_Filter_File_Resize(array(
    ‘directory’ => $filepathSmall,
    ‘width’ => 180,
    ‘height’ => 120,
    ‘keepRatio’ => true,
    )));

    $filepathThumbnail = $filepath . ‘/thumbnail/';
    if (!mkdir($filepathThumbnail, 0, true))
    {
    throw new Zend_Controller_Action_Exception(‘Failed to create folders for Small…’);
    }
    $filterImage->appendFilter(new Skoch_Filter_File_Resize(array(
    ‘directory’ => $filepathThumbnail,
    ‘width’ => 105,
    ‘height’ => 70,
    ‘keepRatio’ => true,
    )));

    for ($x = 0; $x images->getMultiFile(); $x++)
    {
    $xt = @pathinfo($adapter->getFileName(‘images_’ . $x . ‘_’), PATHINFO_EXTENSION);

    $adapter->clearFilters();

    $adapter->addFilter(‘Rename’, array(
    ‘target’ => sprintf(‘%d_%d.%s’, $id, ($x + 1), $xt),
    ‘overwrite’ => true
    ))
    ->addFilter($filterImage);

    $adapter->receive(‘images_’ . $x . ‘_’);
    $item->photo = $values[‘status_id’];
    $item->save();
    }

    $this->_helper->getHelper(‘FlashMessenger’)
    ->addMessage(‘Property successfully created!’);
    $this->_redirect(‘/admin/property/success’);
    }
    }
    }

  11. hi

    I am using your library to resize the image but it gives fatatl error
    “in Class ‘Skoch_Filter_File_Resize’ not found in”
    how to give absolute path.. as i have to include this in my form.

    i have tried using the absolute path but it didnt work.
    Please help me out. Asap as I am new to zend

    • hi pankhi sagte!
      1 – add in your “application.ini” file this : autoloaderNamespaces[] = “Skoch_”.
      2 – unzip downloaded file in “library” folder : “library”->”Skoch”->”Filter”->”File”-> etc…
      3 – use in your code:
      3a) create filter : $filterImage = new Zend_Filter();
      3b) set filter : $filterImage->appendFilter(new Skoch_Filter_File_Resize(array(
      ‘directory’ => $filepath,
      ‘width’ => 900,
      ‘height’ => 600,
      ‘keepRatio’ => true,
      ‘keepSmaller’ => false
      )));
      3c) add filter to adapter : $adapter->addFilter(‘Rename’, array(
      ‘target’ => sprintf(‘%d_%d.%s’, $id, $r, $xt),
      ‘overwrite’ => true
      ))
      ->addFilter($filterImage);

      thats all!!!
      I add crop method to this filter, if you need I can send you

          • Added a comment with my solution to cropping to your gitHub issue. The Markup is a little messed up, but the code is there.

            Thanks for your work.

        • Stefan added crop functionality in the meantime, see GIT repository.

          Works with the new option ‘cropToFit’ => true

          Behaviour is that croptToFit causes keepAspect and keepSmaller to be ignored. Or you could say it forces keepAspect = true and keepSmaller = false. Therefore using cropToFit will always result in an image of given width, and height as in the Filter options.

          If cropping is needed, it is kept to minimum cropping possible, meaning either left/right or top/bottom is cropped, never both for the same image. Cropping is done in equal measure at top/bottom or left/right keeping the biggest possible portion of the center of the image.

          Example:
          $photo->addFilter(new Skoch_Filter_File_Resize(array(
          ‘width’ => 200,
          ‘height’ => 300,
          ‘cropToFit’ => true,
          )));

  12. Hi,
    I am trying your filter to re-size the image. I am trying to re-size an image three times. For that I used filter chain. But it’s just taking the first appended filter, not the rest of twos. I am pasting my code, so that you will clearly understand what I mean

    $filterChain = new Zend_Filter();
    $filterChain->appendFilter(new Skoch_Filter_File_Resize(array(
    ‘directory’ => APPLICATION_PATH . “/../public/images/marketplace/ads/small”,
    ‘width’ => 160,
    ‘height’ => 160,
    ‘keepRatio’ => true,
    )));

    $filterChain->appendFilter(new Skoch_Filter_File_Resize(array(
    ‘directory’ => APPLICATION_PATH . “/../public/images/marketplace/ads/medium”,
    ‘width’ => 240,
    ‘height’ => 240,
    ‘keepRatio’ => true,
    )));

    $filterChain->appendFilter(new Skoch_Filter_File_Resize(array(
    ‘directory’ => APPLICATION_PATH . “/../public/images/marketplace/ads/large”,
    ‘width’ => 480,
    ‘height’ => 480,
    ‘keepRatio’ => true,
    )));

    $image = new Zend_Form_Element_File(‘image’);
    $image->setLabel(‘Upload An Image*’)
    ->addFilter($filterChain)
    ->addValidator(‘Extension’, false, ‘jpg,png,gif’)
    ->setRequired(true);

    This I am using in the form only, not in controller. So the images with different sizes should go to different directories, that I have provided. But in each directory(small, medium, large) same image(the small one) is uploading. If I delete the filter for small image, then it’s uploading medium size image. I tried a lot to figure it out. Please help me in this.

    • Hello stefan,

      Sorry for being a bother. I have managed to get your script to work on my site. I downloaded it from your git repository and I am having the following structure on my application.

      +application
      +libraries/
      -Engine/
      -Skoch
      +Facebook
      +Zend
      +OFC
      +Scaffold
      +modules

      This is not quite the full directory structure. Well, when I use the resize then rename filter it works fine but it outputs just the image name resize-rename without the respective extension.

      I also get the following error when I use multiple images and this is the functionality I need most.

      Fatal error: Call to undefined method Zend_Filter::appendFilter() in /home/clikpals/public_html/clikpals/application/modules/Wall/Form/Image.php on line 49

      Could you please help me with this.

      Thanks.

  13. Hello,

    Great code. I was wondering if you could include a filter that optimizes the images resized for faster loading page views. I know the reduction of size is already doing a lot in optimizing the image but I was thinking of reducing the file size further by using lossless image compression, reduction of image unused colors etc

    • Hi, lossless image compression results in bigger images. Reduction of color helps mostly only with gif compression.
      If you really want smaller images with better quality spread the word about webp https://developers.google.com/speed/webp/ format developed by google. Alas, only Chrome supports it currently :(

      You could add an option to save in a specified format though. So you will always have jpg/png/gif images as output. This would help with uploaded .bmp, .tif, etc files.

      You could also write a new Adapter for other libs and test if those compress better than Gd like ImageMagick.

      Regards
      Armin

  14. Hi
    i am new to zend..in my code multiple image is uploading through ajax.

    i put ur filter folder and do changes in ini folder .image is uploading but not resizing .below is my controller

    function parkPictureAction(){

    if($this->getRequest()->isXmlHttpRequest()){
    $basePath=”../public/park_picture/”;
    $random_folder = $this->randNum();
    if(!empty($_FILES[‘park_picture’])){
    mkdir ( $basePath . $random_folder, 0777 );
    foreach ( $_FILES [‘park_picture’] as $file_to_upload ) :
    $folderPath = $basePath . $random_folder;
    $adapter = new Zend_File_Transfer_Adapter_Http ();
    $adapter->setDestination ( $basePath.$random_folder );
    $adapter->receive ();
    $filterImage = new Zend_Filter();
    $filterImage->appendFilter(new Skoch_Filter_File_Resize(array(
    ‘directory’ => $basePath.$random_folder,
    ‘width’ => 50,
    ‘height’ => 50,
    ‘keepRatio’ => true
    )));

    endforeach;
    echo Zend_Json::encode ( array (‘msg’ => 1,
    ‘pth’ => $random_folder,
    ‘uploaded_pic’ => $this->getDirectoryimages( $basePath.$random_folder,’/park_picture/’.$random_folder.’/’),
    ) );
    }
    }
    $this->_helper->layout->disableLayout (true);
    $this->_helper->viewRenderer->setNoRender ( true );
    }

  15. Works great. I would just appreciate one tip:

    I have an uploaded image, and I want to save it twice – once as “image-m.jpg” and second time as “image-l.jpg”, both in the same folder. How can I achieve that?

    • I don’t know my own library well enough anymore, but I think this is not possible. It seems I use a fixed name and one has to use folders for different versions. But of course you could add this requirement and start a pull request on github.

      I have to admit I do not maintain this anymore… If I’d work on PHP, I guess I’d start a total new library, because this one seems to have some errors, due to me misunderstanding the Zend filter chain. However, we also have ZF2 now and I do not do anything with PHP at the moment and so on.

      So long story short, you would have to implement it yourself or use the different-folders-same-filename-approach.

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>