在PHP中延迟while循环以防止内存耗尽


Delay a while loop in PHP to prevent memory exhaustion?

我有一个非常简单的图像大小调整脚本,它在1000个文件夹中循环,每个文件夹中有不同数量的图像。在我的测试服务器上,没有太多图像,所以运行良好。。。但生活在大约10万张图片中。。。它会遇到内存问题。php内存已经设置为384M,但我宁愿让脚本一次只处理10个文件夹,停止一段时间,然后重新开始。如何在while循环中做到这一点?也许可以使用睡眠功能?

给…

class DevTestHelper {

//This will Go through all full size images and resize images into requested folder
function resizeGalleryImages($destFolder, $width, $height, $isCrop = false) {
    $count = 1000;
    $source = JPATH_SITE."/images/gallery/full";
    $dest = JPATH_SITE."/images/gallery/".$destFolder;
    echo 'Processing Images<br>';
    //Loop through all 1000 folders 0 to 999 in the /full dir
    for($i=0; $i < $count;$i++) {
        $iPath = $source.'/'.$i;
        if(is_dir($iPath)) {
                $h = opendir($iPath);
                //Loop through all the files in numbered folder
                while ($entry = readdir($h)) {
             //only read files
                    if ($entry != "." && $entry != ".." &&        !is_dir($entry)) {
              $img = new GImage($source.'/'.$i.'/'.$entry);
                        $tmp = $img->resize($width, $height, true, 3);
                        if($isCrop) {
                            $tmpWidth = $tmp->getWidth();
                            $tmpHeight = $tmp->getHeight();
                            $xOffset = ($tmpWidth - $width) / 2;
                            $yOffset = ($tmpHeight - $height) / 2;
                            $tmp->crop($width, $height, $xOffset, $yOffset, false, 3);
                        }
                        $destination = $dest.'/'.$i.'/'.$entry;
                        if(!$tmp->toFile($destination)) {
                            echo 'error in creating resized image at: '.$destination.'<br>';
                      }
                      flush();
          ob_flush();
          sleep(10);
                      echo $entry ;
                      //echo $entry.'<br>';
            }
                }
            echo 'Processed: '.$i.' Folder<br>';
        }
    }

谢谢你的建议。

/**
* Class to manipulate an image.
*
* @package      Gallery.Libraries
* @subpackage   Media
* @version      1.0
*/
class GImage extends JObject
{
/**
 * The image handle
 *
 * @access  private
 * @var     resource
 */
var $_handle = null;
/**
 * The source image path
 *
 * @access  private
 * @var     string
 */
var $_path = null;
var $_support = array();
/**
 * Constructor
 *
 * @access  public
 * @return  void
 * @since   1.0
 */
function __construct($source=null)
{
    // First we test if dependencies are met.
    if (!GImageHelper::test()) {
        $this->setError('Unmet Dependencies');
        return false;
    }
    // Determine which image types are supported by GD.

    $info = gd_info();
    if ($info['JPG Support']) {
        $this->_support['JPG'] = true;
    }
    if ($info['GIF Create Support']) {
        $this->_support['GIF'] = true;
    }
    if ($info['PNG Support']) {
        $this->_support['PNG'] = true;
    }
    // If the source input is a resource, set it as the image handle.
    if ((is_resource($source) && get_resource_type($source) == 'gd')) {
        $this->_handle = &$source;
    } elseif (!empty($source) && is_string($source)) {
        // If the source input is not empty, assume it is a path and populate the image handle.
        //Andy - Big freaking cockroach: if file is wrong type or doesn't even exist the error is not handled.
        $this->loadFromFile($source);
    }
}
function crop($width, $height, $left, $top, $createNew = true, $scaleMethod = JXIMAGE_SCALE_INSIDE)
{
    // Make sure the file handle is valid.
    if ((!is_resource($this->_handle) || get_resource_type($this->_handle) != 'gd')) {
        $this->setError('Invalid File Handle');
        return false;
    }
    // Sanitize width.
    $width = ($width === null) ? $height : $width;
    if (preg_match('/^[0-9]+('.[0-9]+)?'%$/', $width)) {
        $width = intval(round($this->getWidth() * floatval(str_replace('%', '', $width)) / 100));
    } else {
        $width = intval(round(floatval($width)));
    }
    // Sanitize height.
    $height = ($height === null) ? $width : $height;
    if (preg_match('/^[0-9]+('.[0-9]+)?'%$/', $height)) {
        $height = intval(round($this->getHeight() * floatval(str_replace('%', '', $height)) / 100));
    } else {
        $height = intval(round(floatval($height)));
    }
    // Sanitize left.
    $left = intval(round(floatval($left)));
    // Sanitize top.
    $top = intval(round(floatval($top)));
    // Create the new truecolor image handle.
    $handle = imagecreatetruecolor($width, $height);
    // Allow transparency for the new image handle.
    imagealphablending($handle, false);
    imagesavealpha($handle, true);
    if ($this->isTransparent()) {
        // Get the transparent color values for the current image.
        $rgba = imageColorsForIndex($this->_handle, imagecolortransparent($this->_handle));
        $color = imageColorAllocate($this->_handle, $rgba['red'], $rgba['green'], $rgba['blue']);
        // Set the transparent color values for the new image.
        imagecolortransparent($handle, $color);
        imagefill($handle, 0, 0, $color);
        imagecopyresized(
            $handle,
            $this->_handle,
            0, 0,
            $left,
            $top,
            $width,
            $height,
            $width,
            $height
        );
    } else {
        imagecopyresampled(
            $handle,
            $this->_handle,
            0, 0,
            $left,
            $top,
            $width,
            $height,
            $width,
            $height
        );
    }
    // If we are cropping to a new image, create a new GImage object.
    if ($createNew)
    {
        // Create the new GImage object for the new truecolor image handle.
        $new = new GImage($handle);
        return $new;
    } else
    {
        // Swap out the current handle for the new image handle.
        $this->_handle = &$handle;
        return true;
    }
}
  function filter($type)
{
    // Initialize variables.
    $name = preg_replace('#[^A-Z0-9_]#i', '', $type);
    $className = 'GImageFilter_'.ucfirst($name);
    if (!class_exists($className))
    {
        jimport('joomla.filesystem.path');
        if ($path = JPath::find(GImageFilter::addIncludePath(), strtolower($name).'.php'))
        {
            require_once $path;
            if (!class_exists($className)) {
                $this->setError($className.' not found in file.');
                return false;
            }
        }
        else {
            $this->setError($className.' not supported. File not found.');
            return false;
        }
    }
    $instance = new $className;
    if (is_callable(array($instance, 'execute')))
    {
        // Setup the arguments to call the filter execute method.
        $args = func_get_args();
        array_shift($args);
        array_unshift($args, $this->_handle);
        // Call the filter execute method.
        $return = call_user_func_array(array($instance, 'execute'), $args);
        // If the filter failed, proxy the error and return false.
        if (!$return) {
            $this->setError($instance->getError());
            return false;
        }
        return true;
    }
    else {
        $this->setError($className.' not valid.');
        return false;
    }
}
function getHeight()
{
    return imagesy($this->_handle);
}
function getWidth()
{
    return imagesx($this->_handle);
}
function isTransparent()
{
    // Make sure the file handle is valid.
    if ((!is_resource($this->_handle) || get_resource_type($this->_handle) != 'gd')) {
        $this->setError('Invalid File Handle');
        return false;
    }
    return (imagecolortransparent($this->_handle) >= 0);
}
function loadFromFile($path)
{
    // Make sure the file exists.
    if (!JFile::exists($path)) {
        $this->setError('File Does Not Exist');
        return false;
    }
    // Get the image properties.
    $properties = GImageHelper::getProperties($path);
    if (!$properties) {
        return false;
    }
    // Attempt to load the image based on the MIME-Type
    switch ($properties->get('mime'))
    {
        case 'image/gif':
            // Make sure the image type is supported.
            if (empty($this->_support['GIF'])) {
                $this->setError('File Type Not Supported');
                return false;
            }
            // Attempt to create the image handle.
            $handle = @imagecreatefromgif($path);
            if (!is_resource($handle)) {
                $this->setError('Unable To Process Image');
                return false;
            }
            $this->_handle = &$handle;
            break;
        case 'image/jpeg':
            // Make sure the image type is supported.
            if (empty($this->_support['JPG'])) {
                $this->setError('File Type Not Supported');
                return false;
            }
            // Attempt to create the image handle.
            $handle = @imagecreatefromjpeg($path);
            if (!is_resource($handle)) {
                $this->setError('Unable To Process Image');
                return false;
            }
            $this->_handle = &$handle;
            break;
        case 'image/png':
            // Make sure the image type is supported.
            if (empty($this->_support['PNG'])) {
                $this->setError('File Type Not Supported');
                return false;
            }
            // Attempt to create the image handle.
            $handle = @imagecreatefrompng($path);
            if (!is_resource($handle)) {
                $this->setError('Unable To Process Image');
                return false;
            }
            $this->_handle = &$handle;
            break;
        default:
            $this->setError('File Type Not Supported');
            return false;
            break;
    }
    // Set the filesystem path to the source image.
    $this->_path = $path;
    return true;
}
function resize($width, $height, $createNew = true, $scaleMethod = JXIMAGE_SCALE_INSIDE)
{
    // Make sure the file handle is valid.
    if ((!is_resource($this->_handle) || get_resource_type($this->_handle) != 'gd')) {
        $this->setError('Invalid File Handle');
        return false;
    }
    // Prepare the dimensions for the resize operation.
    $dimensions = $this->_prepareDimensions($width, $height, $scaleMethod);
    if (empty($dimensions)) {
        return false;
    }
    //var_dump($dimensions);
    // Create the new truecolor image handle.
    $handle = imagecreatetruecolor($dimensions['width'], $dimensions['height']);
    // Allow transparency for the new image handle.
    imagealphablending($handle, false);
    imagesavealpha($handle, true);
    if ($this->isTransparent()) {
        // Get the transparent color values for the current image.
        $rgba = imageColorsForIndex($this->_handle, imagecolortransparent($this->_handle));
        $color = imageColorAllocate($this->_handle, $rgba['red'], $rgba['green'], $rgba['blue']);
        // Set the transparent color values for the new image.
        imagecolortransparent($handle, $color);
        imagefill($handle, 0, 0, $color);
        imagecopyresized(
            $handle,
            $this->_handle,
            0, 0, 0, 0,
            $dimensions['width'],
            $dimensions['height'],
            $this->getWidth(),
            $this->getHeight()
        );
    } else {
        imagecopyresampled(
            $handle,
            $this->_handle,
            0, 0, 0, 0,
            $dimensions['width'],
            $dimensions['height'],
            $this->getWidth(),
            $this->getHeight()
        );
    }
    // If we are resizing to a new image, create a new GImage object.
    if ($createNew)
    {
        // Create the new GImage object for the new truecolor image handle.
        $new = new GImage($handle);
        return $new;
    } else
    {
        // Swap out the current handle for the new image handle.
        $this->_handle = &$handle;
        return true;
    }
}
function toFile($path, $type = IMAGETYPE_JPEG, $options=array())
{
    switch ($type)
    {
        case IMAGETYPE_GIF:
            $ret = imagegif($this->_handle, $path);
            break;
        case IMAGETYPE_PNG:
            $ret = imagepng($this->_handle, $path, (array_key_exists('quality', $options)) ? $options['quality'] : 0);
            break;
        case IMAGETYPE_JPEG:
        default:
            $ret = imagejpeg($this->_handle, $path, (array_key_exists('quality', $options)) ? $options['quality'] : 100);
            break;
    }
    return $ret;
}
function display() {
    //header('Content-type: image/jpeg');
    imagejpeg($this->_handle,'',100);
}
function _prepareDimensions($width, $height, $scaleMethod)
{
    // Sanitize width.
    $width = ($width === null) ? $height : $width;
    if (preg_match('/^[0-9]+('.[0-9]+)?'%$/', $width)) {
        $width = intval(round($this->getWidth() * floatval(str_replace('%', '', $width)) / 100));
    } else {
        $width = intval(round(floatval($width)));
    }
    // Sanitize height.
    $height = ($height === null) ? $width : $height;
    if (preg_match('/^[0-9]+('.[0-9]+)?'%$/', $height)) {
        $height = intval(round($this->getHeight() * floatval(str_replace('%', '', $height)) / 100));
    } else {
        $height = intval(round(floatval($height)));
    }
    $dimensions = array();
    if ($scaleMethod == JXIMAGE_SCALE_FILL)
    {
        $dimensions['width'] = $width;
        $dimensions['height'] = $height;
    }
    elseif ($scaleMethod == JXIMAGE_SCALE_INSIDE || $scaleMethod == JXIMAGE_SCALE_OUTSIDE)
    {
        $rx = $this->getWidth() / $width;
        $ry = $this->getHeight() / $height;
        if ($scaleMethod == JXIMAGE_SCALE_INSIDE)
            $ratio = ($rx > $ry) ? $rx : $ry;
        else
            $ratio = ($rx < $ry) ? $rx : $ry;
        $dimensions['width']    = round($this->getWidth() / $ratio);
        $dimensions['height']   = round($this->getHeight() / $ratio);
    }
    else {
        $this->setError('Invalid Fit Option');
        return false;
    }
    return $dimensions;
}
}
 class GImageFilter extends JObject
{
/**
 * Add a directory where GImage should search for filters. You may
 * either pass a string or an array of directories.
 *
 * @access  public
 * @param   string  A path to search.
 * @return  array   An array with directory elements
 * @since   1.5
 */
function addIncludePath($path='')
{
    static $paths;
    if (!isset($paths)) {
        $paths = array(dirname(__FILE__).'/image');
    }
    // force path to array
    settype($path, 'array');
    // loop through the path directories
    foreach ($path as $dir)
    {
        if (!empty($dir) && !in_array($dir, $paths)) {
            array_unshift($paths, JPath::clean( $dir ));
        }
    }
    return $paths;
}
function execute()
{
    $this->setError('Method Not Implemented');
    return false;
}
}
class GImageHelper
{
function getProperties($path)
{
    // Initialize the path variable.
    $path = (empty($path)) ? $this->_path : $path;
    // Make sure the file exists.
    if (!JFile::exists($path)) {
        $e = new JException('File Does Not Exist');
        return false;
    }
    // Get the image file information.
    $info = @getimagesize($path);
    if (!$info) {
        $e = new JException('Unable To Get Image Size');
        return false;
    }
    // Build the response object.
    $result = new JObject;
    $result->set('width',       $info[0]);
    $result->set('height',      $info[1]);
    $result->set('type',        $info[2]);
    $result->set('attributes',  $info[3]);
    $result->set('bits',        @$info['bits']);
    $result->set('channels',    @$info['channels']);
    $result->set('mime',        $info['mime']);
    return $result;
}
function test()
{
    return (function_exists('gd_info') &&            function_exists('imagecreatetruecolor'));
}

}

在我看来,您有内存泄漏,当使用new GImage创建$img时,可能会将图像读取到您从未释放的内存中。您复制调整大小的图像:

$tmp = $img->resize($width, $height, true, 3);

但下次循环时,您将再次使用$img变量创建一个新的GImage

GIMage是否具有某种closecleanup功能?如果是,请在每次循环结束时执行此操作,然后再调用new GImage

closeGImage($img);

更新:

在回答您最新的问题时,看起来您可以做两件事之一:

  • $img->resize()的第三个参数更改为false,因为这将停止在调整图像大小时创建新的GImage。

  • 作为GImage类的一部分,编写自己的图像销毁函数。由于GImage基于GD库,因此可以使用imagedestroy函数,请参阅此处。


更新2:

好的,下面是你的评论。。。要创建销毁图像函数,您需要在GImage类中添加以下函数:

function closeImage()
{
    imagedestroy($this->_handle);
}

现在,在devtest.php中for循环的底部,添加对以下函数的调用:

...
ob_flush();
sleep(10);
echo $entry ;
$img->closeImage();
...
  • 尝试使用sleepusleep以避免CPU过载
  • GImage可能有dispose()destroy()方法来避免内存保留
  • 您也可以进行批量处理