php处理图片出现内存溢出(Allowed memory size of 134217728 bytes exhausted)

发布于:2025-02-22 ⋅ 阅读:(21) ⋅ 点赞:(0)

错误

最近做图片上传功能时发现上传某些图片时报内存溢出错误。如下所示:

{
  "code": 0,
  "msg": "Allowed memory size of 134217728 bytes exhausted (tried to allocate 24576 bytes)",
  "data": {
    "code": 0,
    "line": 1806,
    "file": "\myweb\\ytems\app\\common.php",
    "message": "Allowed memory size of 134217728 bytes exhausted (tried to allocate 24576 bytes)",
    "trace": [
      {
        "function": "appShutdown",
        "class": "think\\initializer\\Error",
        "type": "->",
        "args": []
      }
    ]
  }
}

初步检查图片详情,发现图片并不大为861KB,服务器配置的内存上限是128MB,接口上传图片限制是2MB。排除图片大小限制问题。但发现此图片的分辨率为 5283x7727,初步怀疑是图片分辨率过大导致。

解决:

1、找出原因

通过对多张不同大小分辨率的图片进行测试发现果然是大分辨率的图片才会导致此报错,通过排查程序代码发现是在调用 imagecreatefromjpeg 图片处理函数时报的错误。

查看官方文档,才发现imagecreatefromjpeg在动态创建一个新图像时,是根据图片的分辨率(宽和高)在内存中生成一个新图像。

因此虽然用户上传的图片只有为861KB,但分辨率却高达 5283x7727,按照官方手册给出的公式可算出内存已达到(5283*7727*24/8*其它因素>117MB),加上其他程序消耗内存。很容易超过128MB内存上限,才导致内存溢出。

官方手册

imagecreatefromjpeg创建图像时占用内存公式:

总字节数=宽*高*每像素所占字节数*其它因素  (即 5283*7727*24/8*其它因素)

每像素所占字节数有2种计算方式:

每像素所占字节数
=位深/ 8
=色深(bits) *图像的通道(channels) / 8

(php使用getimagesize可以获取jpg图片的bits和channels,位深:如下图,在电脑上查看图片的属性->详细信息)

由此可知,imagecreatefromjpeg创建新图像的动作是在内存中实现,因此图像的宽和高很大程度上决定了内存的消耗。

2、在图片上传时限制图片的像素大小

相关函数如下:

/**
 * 校验图片像素大小

 * @param $sourceFile 原始图片文件
 * @param $$allowed_size 允许的像素大小
 * @return array
 * @throws Exception
 */
function valImage($sourceFile, $allowed_size=4000) {
    ini_set('memory_limit', '256M');

    // 检查GD库是否可用
    if (!extension_loaded('gd') || !function_exists('gd_info')) {
        //throw new Exception('GD库未安装');
        return ['code'=>4001,'msg'=>'GD库未安装'];
    }

    // 验证源文件
    if (!file_exists($sourceFile)) {
      //  throw new Exception('源文件不存在');
        return ['code'=>4002,'msg'=>'源文件不存在'];
    }

    // 获取图片信息
    $imageInfo = @getimagesize($sourceFile);
    if (!$imageInfo) {
        //throw new Exception('无效的图片文件');
        return ['code'=>4003,'msg'=>'无效的图片文件'];
    }

//    print_r($imageInfo);
     if($imageInfo[0] > $allowed_size|| $imageInfo[1]  > $allowed_size){
         return ['code'=>4000,'msg'=>'图片分辨率不可大于'.$allowed_size.'*'.$allowed_size.'像素'];
     }

     return ['code'=>200,'msg'=校验通过'];

}