false, 'error' => '上传参数不对']; } switch ($file['error']) { case UPLOAD_ERR_OK: break; case UPLOAD_ERR_NO_FILE: return ['ok' => false, 'error' => '']; case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: return ['ok' => false, 'error' => '图片太大了(上限 5MB)']; default: return ['ok' => false, 'error' => '上传失败(错误码 ' . (int)$file['error'] . ')']; } if ($file['size'] > self::MAX_BYTES) { return ['ok' => false, 'error' => '图片太大了(上限 5MB)']; } $finfo = new \finfo(FILEINFO_MIME_TYPE); $mime = $finfo->file($file['tmp_name']); $allow = ['image/jpeg' => 'imagecreatefromjpeg', 'image/png' => 'imagecreatefrompng', 'image/gif' => 'imagecreatefromgif', 'image/webp' => 'imagecreatefromwebp']; if (!isset($allow[$mime])) { return ['ok' => false, 'error' => '只支持 JPG / PNG / GIF / WebP 图片']; } $src = $allow[$mime]($file['tmp_name']); if (!$src) { return ['ok' => false, 'error' => '图片读不出来']; } $w = imagesx($src); $h = imagesy($src); // 居中裁成正方形 $side = min($w, $h); $sx = (int)(($w - $side) / 2); $sy = (int)(($h - $side) / 2); $canvas = imagecreatetruecolor(self::SIZE, self::SIZE); // PNG/GIF 的透明区转成纸白,不然头像会是黑底 $bg = imagecolorallocate($canvas, 253, 251, 245); imagefilledrectangle($canvas, 0, 0, self::SIZE, self::SIZE, $bg); imagecopyresampled($canvas, $src, 0, 0, $sx, $sy, self::SIZE, self::SIZE, $side, $side); $dir = FB_UPLOADS . '/avatars'; if (!is_dir($dir)) { mkdir($dir, 0755, true); } $name = bin2hex(random_bytes(12)) . '.jpg'; if (!imagejpeg($canvas, $dir . '/' . $name, 88)) { imagedestroy($canvas); imagedestroy($src); return ['ok' => false, 'error' => '保存失败']; } imagedestroy($canvas); imagedestroy($src); return ['ok' => true, 'file' => 'avatars/' . $name]; } /** 头像访问地址(无头像时回退到内置剪影,保证永远不出现破图) */ public static function avatarUrl(?string $rel): string { if ($rel && is_file(FB_UPLOADS . '/' . $rel)) { $p = FB_UPLOADS . '/' . $rel; // 注意:filemtime() 返回 int,本文件开了 strict_types,直接喂给 md5() 会 TypeError return 'uploads/' . $rel . '?v=' . substr(md5(filemtime($p) . filesize($p)), 0, 8); } return asset('/assets/avatar-default.svg'); } public static function remove(?string $rel): void { if (!$rel) { return; } $p = FB_UPLOADS . '/' . $rel; if (is_file($p)) { @unlink($p); } } }