我写了一个图片上传,并且生成缩略图的小程序,本以为就此万事大吉,没想到啊,实际使用中,图片文件一旦超过2M,缩略时内存使用量飙升,真的是飙升啊,一点都不夸张的。2M以内时使用内存才20M以内,大部分虚拟主机都能跑跑,一旦超越2M……比如说,3.5M大约要吃130M内存,5M内存使用量甚至到了290M!!
因为此程序主要是用于大图上传的,而且必须是后台生成缩略图,是冲印网站给普通用户用的。麻烦啊,用了几种缩略类,情况是一摸一样的,内存消耗降不下来。哪位有什么解决办法啊,万分感谢!以下是我的代码:
---class/image.class.php---
<?php      
   
class CreatMiniature    
{    
    //公共变量    
    var $srcFile="";        //原图    
    var $echoType;            //输出图片类型,link--不保存为文件;file--保存为文件    
    var $im="";                //临时变量    
    var $srcW="";            //原图宽    
    var $srcH="";            //原图高    
   
    //设置变量及初始化    
    function SetVar($srcFile,$echoType)    
    {    
        if (!file_exists($srcFile)){    
            echo '源图片文件不存在!';    
            exit();    
        }    
        $this->srcFile=$srcFile;    
        $this->echoType=$echoType;    
   
        $info = "";    
        $data = GetImageSize($this->srcFile,$info);    
        switch ($data[2])     
        {    
         case 1:    
           if(!function_exists("imagecreatefromgif")){    
            echo "你的GD库不能使用GIF格式的图片,请使用Jpeg或PNG格式!<a href='javascript:go(-1);'>返回</a>";    
            exit();    
           }    
           $this->im = ImageCreateFromGIF($this->srcFile);    
           break;    
        case 2:    
          if(!function_exists("imagecreatefromjpeg")){    
           echo "你的GD库不能使用jpeg格式的图片,请使用其它格式的图片!<a href='javascript:go(-1);'>返回</a>";    
           exit();    
          }    
          $this->im = ImageCreateFromJpeg($this->srcFile);        
          break;    
        case 3:    
          $this->im = ImageCreateFromPNG($this->srcFile);        
          break;    
      }    
      $this->srcW=ImageSX($this->im);    
      $this->srcH=ImageSY($this->im);     
    }    
        
    //生成扭曲型缩图    
    function Distortion($toFile,$toW,$toH)    
    {    
        $cImg=$this->CreatImage($this->im,$toW,$toH,0,0,0,0,$this->srcW,$this->srcH);    
        return $this->EchoImage($cImg,$toFile);    
        ImageDestroy($cImg);    
    }    
        
    //生成按比例缩放的缩图    
    function Prorate($toFile,$toW,$toH)    
    {    
        $toWH=$toW/$toH;    
        $srcWH=$this->srcW/$this->srcH;    
        if($toWH<=$srcWH)    
        {    
            $ftoW=$toW;    
            $ftoH=$ftoW*($this->srcH/$this->srcW);    
        }    
        else   
        {    
              $ftoH=$toH;    
              $ftoW=$ftoH*($this->srcW/$this->srcH);    
        }    
        if($this->srcW>$toW||$this->srcH>$toH)    
        {    
            $cImg=$this->CreatImage($this->im,$ftoW,$ftoH,0,0,0,0,$this->srcW,$this->srcH);    
            return $this->EchoImage($cImg,$toFile);    
            ImageDestroy($cImg);    
        }    
        else   
        {    
            $cImg=$this->CreatImage($this->im,$this->srcW,$this->srcH,0,0,0,0,$this->srcW,$this->srcH);    
            return $this->EchoImage($cImg,$toFile);    
            ImageDestroy($cImg);    
        }    
    }    
        
    //生成最小裁剪后的缩图    
    function Cut($toFile,$toW,$toH)    
    {    
          $toWH=$toW/$toH;    
          $srcWH=$this->srcW/$this->srcH;    
          if($toWH<=$srcWH)    
          {    
               $ctoH=$toH;    
               $ctoW=$ctoH*($this->srcW/$this->srcH);    
          }    
          else   
          {    
              $ctoW=$toW;    
              $ctoH=$ctoW*($this->srcH/$this->srcW);    
          }     
        $allImg=$this->CreatImage($this->im,$ctoW,$ctoH,0,0,0,0,$this->srcW,$this->srcH);    
        $cImg=$this->CreatImage($allImg,$toW,$toH,0,0,($ctoW-$toW)/2,($ctoH-$toH)/2,$toW,$toH);    
        return $this->EchoImage($cImg,$toFile);    
        ImageDestroy($cImg);    
        ImageDestroy($allImg);    
    }    
   
    //生成背景填充的缩图    
    function BackFill($toFile,$toW,$toH,$bk1=255,$bk2=255,$bk3=255)    
    {    
        $toWH=$toW/$toH;    
        $srcWH=$this->srcW/$this->srcH;    
        if($toWH<=$srcWH)    
        {    
            $ftoW=$toW;    
            $ftoH=$ftoW*($this->srcH/$this->srcW);    
        }    
        else   
        {    
              $ftoH=$toH;    
              $ftoW=$ftoH*($this->srcW/$this->srcH);    
        }    
        if(function_exists("imagecreatetruecolor"))    
        {    
            @$cImg=ImageCreateTrueColor($toW,$toH);    
            if(!$cImg)    
            {    
                $cImg=ImageCreate($toW,$toH);    
            }    
        }    
        else   
        {    
            $cImg=ImageCreate($toW,$toH);    
        }    
        $backcolor = imagecolorallocate($cImg, $bk1, $bk2, $bk3);        //填充的背景颜色    
        ImageFilledRectangle($cImg,0,0,$toW,$toH,$backcolor);    
        if($this->srcW>$toW||$this->srcH>$toH)    
        {         
            $proImg=$this->CreatImage($this->im,$ftoW,$ftoH,0,0,0,0,$this->srcW,$this->srcH);    
             if($ftoW<$toW)    
             {    
                 ImageCopy($cImg,$proImg,($toW-$ftoW)/2,0,0,0,$ftoW,$ftoH);    
             }    
             else if($ftoH<$toH)    
             {    
                 ImageCopy($cImg,$proImg,0,($toH-$ftoH)/2,0,0,$ftoW,$ftoH);    
             }    
             else   
             {    
                 ImageCopy($cImg,$proImg,0,0,0,0,$ftoW,$ftoH);    
             }    
        }    
        else   
        {    
             ImageCopyMerge($cImg,$this->im,($toW-$ftoW)/2,($toH-$ftoH)/2,0,0,$ftoW,$ftoH,100);    
        }    
        return $this->EchoImage($cImg,$toFile);    
        ImageDestroy($cImg);    
    }    
        
   
    function CreatImage($img,$creatW,$creatH,$dstX,$dstY,$srcX,$srcY,$srcImgW,$srcImgH)    
    {    
        if(function_exists("imagecreatetruecolor"))    
        {    
            @$creatImg = ImageCreateTrueColor($creatW,$creatH);    
            if($creatImg)     
                ImageCopyResampled($creatImg,$img,$dstX,$dstY,$srcX,$srcY,$creatW,$creatH,$srcImgW,$srcImgH);    
            else   
            {    
                $creatImg=ImageCreate($creatW,$creatH);    
                ImageCopyResized($creatImg,$img,$dstX,$dstY,$srcX,$srcY,$creatW,$creatH,$srcImgW,$srcImgH);    
            }    
         }    
         else   
         {    
            $creatImg=ImageCreate($creatW,$creatH);    
            ImageCopyResized($creatImg,$img,$dstX,$dstY,$srcX,$srcY,$creatW,$creatH,$srcImgW,$srcImgH);    
         }    
         return $creatImg;    
    }    
        
    //输出图片,link---只输出,不保存文件。file--保存为文件    
    function EchoImage($img,$to_File)    
    {    
        switch($this->echoType)    
        {    
            case "link":    
                if(function_exists('imagejpeg')) return ImageJpeg($img);    
                else return ImagePNG($img);    
                break;    
            case "file":    
                if(function_exists('imagejpeg')) return ImageJpeg($img,$to_File);    
                else return ImagePNG($img,$to_File);    
                break;    
        }    
    }    
}    
?>
---class/miniature_memory.php---
<?php
function miniatureMemory($uploadfile){
  $imageInfo = getimagesize($uploadfile);
  $memoryNeeded = round(($imageInfo[0] * $imageInfo[1] * $imageInfo['bits'] * $imageInfo['channels'] / 8 + Pow(2, 16)) * 1.8);#echo $memoryNeeded;exit;
                    
  if (function_exists('memory_get_usage') && memory_get_usage() + $memoryNeeded > (integer) ini_get('memory_limit') * pow(1024, 2))
  {                        
    ini_set('memory_limit', (integer) ini_get('memory_limit') + ceil(((memory_get_usage() + $memoryNeeded) - (integer) ini_get('memory_limit') * pow(1024, 2)) / pow(1024, 2)) . 'M');
  }
}
?>

解决方案 »

  1.   

    ---index.php(客户看到的文件)---
    <?php
    session_start();
    if(strlen($_SESSION['UID'])<10){  //重置订单及图片编号
      $_SESSION['UID']=date("YmdHis").(microtime()*1000000);
      $_SESSION['UID']=substr($_SESSION['UID'],0,strlen($_SESSION['UID'])-3);
      $_SESSION['picNo']=1;
    }
    ?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>无标题文档</title>
    <script type="text/javascript">
    <!--
    function checkForm(theForm)
    {
      if(theForm.file.value == "")
      {
      alert ( "请选择需要上传的图片文件" ) ;
          theForm.file.focus();
      return false;
      }else
      {
          theForm.sbt.value='文件上传中...';
      theForm.sbt.disabled=true;
      document.getElementById("pic_form").submit();
      return true;
      }
    }
    -->
    </script>
    </head>
    <FORM id="pic_form" name="pic_form" action="saveload.php" method="post" enctype="multipart/form-data">
    <table width="238" height=100% border="0" cellspacing="0" cellpadding="0">  <tr>
        <td bgcolor="#fde1aa">
          &nbsp;&nbsp;<input name="file" type="file"  size="15" >
          <br>
          <br></td>
      </tr>
      
      <tr>
        <td height="23" background="images/left2.jpg">&nbsp;</td>
      </tr>
      <tr>
        <td align="center" bgcolor="#fde1aa"><br>
        &nbsp;&nbsp;<input name="sbt" id="sbt" type=submit value="上传图片" onClick="JavaScript:return checkForm(this.form);" /><br><br></td>
      </tr>
      <tr>
        <td height="1" align="center"  background="images/left2.jpg"></td>
      </tr>
      <tr>
        <td align="center" bgcolor="#fde1aa">
    <br><br></td>
      </tr>
      <tr>
        <td height="7" align="center" background="images/left3.jpg"></td>
      </tr>
    </table>
    </form>
    ---saveload.php(后台处理程序)---
    <?php
    session_start();
    header("Pragma: no-cache");
    header("Content-Type: text/html; charset=utf-8");
    ?>
    <!--
    <table width="100%" border="0">
      <tr>
        <td colspan="5"><center>
          <img src="images/loading.gif">
        </center></td>
      </tr>
      <tr>
        <td colspan="5"><center>
          文件上传中……
        </center></td>
      </tr>
    </table>
    -->
    <?php
    require_once('class/file_url.class.php');
    $uploaddir=$_SERVER['DOCUMENT_ROOT'].dirname(request_uri())."/users_files/$UID/";if(!is_dir($uploaddir))
      mkdir   ($uploaddir,   0777);  //创建用户文件夹
       $type=array("jpg","gif","png","bmp"); //设置允许上传文件的类型
       $patch="http://".$_SERVER ['HTTP_HOST'].dirname(request_uri()); //程序所在路径
       //获取文件后缀名函数
          function fileext($filename)
        {
            return substr(strrchr($filename, '.'), 1);
        }
      function crname()
    {
            $name = $_SESSION['UID'].'-'.$_SESSION['picNo'] ;
    $_SESSION['picNo']=$_SESSION['picNo']+1;
            return $name;
        }
       $a=strtolower(fileext($_FILES['file']['name']));
       //判断文件类型
       if(!in_array(strtolower(fileext($_FILES['file']['name'])),$type))
         {
            $text=implode(",",$type);
            echo "<script type=\"text/javascript\">alert(\"您只能上传以下类型文件:$text\");</script>";
    echo "<script type =\"text/javascript\">history.go(-1);</script>";
         }
       //生成目标文件的文件名   
       else{
        $filename=explode(".",$_FILES['file']['name']);
            do
            {
    $filename[0]=crname(); //设置时间文件名
                $name=implode(".",$filename);
                $uploadfile=$uploaddir.$name;
            }
       while(file_exists($uploadfile));
       #echo $uploadfile;exit;
          if (move_uploaded_file($_FILES['file']['tmp_name'],$uploadfile)){
     $arr=getimagesize($uploadfile);
     $max_px=1600;
             if($arr[0]>=$max_px || $arr[1]>=$max_px){  //对大图片生成缩略图
               require_once('class/image.class.php');
       require_once('class/miniature_memory.php');
       miniatureMemory($uploadfile);
               $show_pic=new CreatMiniature();
           $show_pic->SetVar($uploadfile,'file');
           $show_pic->Prorate($uploaddir.'temp'.$name,$max_px,$max_px);
     }
          }
          else echo 'UpDate Error (02)';
       }
    ?>
      

  2.   

    哦,想到一个办法,缩略图用JS实时生成好了,不知道可行性如何,测试去了……
    大家等我好消息吧!
    PHP有什么好解决办法了,也请不吝赐教!
      

  3.   

    现成的缩略图生成类库:
    phpThumb - 功能很强大,如何强大还是自己去体会吧。 
      

  4.   

    2007-12-28 17:16 
    如果在虚拟空间上使用phpThumb的话,要注意内存大小,有时候如果输入图片过大或其他要求过高,会导致服务器返回内存不足的错误,因为虚拟空间对每个客户的内存分配是有限制的。有种不祥的预感~
      

  5.   

    绝对不是这个原因。
    2m的jpeg大概也就3k*2k左右大,也就18m的样子。就算是gd的bug也不会是因为“将图片解压成位图“这个原因。纯粹在误人子弟。换imagick试。
      

  6.   

    速去学习ImageMagick.我现在一台服务器用ImageMagick每天处理2W张从3M~20M原图,然后生成六种规格缩略图。
      

  7.   

    ImageMagick +2
    不过要服务器支持
      

  8.   

    这么多人支持ImageMagick?
    问题是要服务器支持怎么办??
      

  9.   

    经实测,phpThumb比我这个更难支持大图……
      

  10.   

    后天,我必须要完成这个了,难道ImageMagick是唯一选择了吗?难道GD真的这么没用,PHP5才出的GD2啊,PHP不是说很强悍吗?为什么我一会做个文件上传进度条不行,一会做个缩略图就这么多问题?
      

  11.   

    跟客户签的合同一再延期了,唉。
    不是不能装ImageMagick,问题是给他的是虚拟主机,装那玩意BOSS也不会同意的!
    可是又答应过客户要提供这么大图片的预览功能,难道看全图?估计要死翘翘了
      

  12.   

    晕了,刚测试发现换了虚拟主机上,比本机测试更多了个问题:超过2.6M左右的文件,居然还无法上传!http://www.posterjoy.com/phpinfo.php
    这是环境配置,没发现什么问题啊,昨晚一怒之下把upload_max_filesize改到了200M!问题依旧,上传2.65M文件成功,上传2.88M文件失败!即使换成了FLASH上传,到了PHP处理部分就过不去了。move_uploaded_file失败。没想到这么一个本以为很简单的问题,会困扰我这么久
      

  13.   

    问题搞定,总结如下:1、虚拟机会遇到很多问题,不仅是php.ini配置这么简单,还有虚拟机管理软件本身也有许多限制。2、大容量图片的缩略处理,直接用GD库不是很方便,太多问题难以处理。GD库只在一般需求的情况下很方便。ImageMagick是否强一点目前尚不清楚,不过很感谢14楼提供的宝贵线索,值得我去研究。3、phpThumb不好用,每次浏览都产生新的小图,太浪费资源,且每次重新生成缩略,这种模式不科学。基本无法满足正常商用。