前言
嵌入外部链接图像,虽可节省本地空间,但也盗刷了他人服务器(或CDN)的流量,这种行为也被称为”盗链”,虽然我们在发表文章主动上传图像即可避免此类问题,但如果是一个开放式的博客(或论坛),就很难避免此类情况的发生,因此这需要可自动将外链图像保存至本地的方法。
正题
<?php
function ecp_save_post($post_id, $post) {
global $wpdb;
if ($post->post_status == 'publish') {
// 匹配文章内容内的图片 URL
$p = '/<img.*[\s]src=[\"|\'](.*)[\"|\'].*>/iU';
$num = preg_match_all($p, $post->post_content, $matches);
if ($num) {
$wp_upload_dir = wp_upload_dir();
$default_image_url = 'https://example.com/post/default-image.webp'; // 默认图像
set_time_limit(0);
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
$ecp_options = $_SERVER['HTTP_HOST'];
// 定义 MIME 类型映射为文件扩展名
$ext_map = array(
'image/jpeg' => 'jpg',
'image/jfif' => 'jfif',
'image/png' => 'png',
'image/bmp' => 'bmp',
'image/gif' => 'gif',
'image/webp' => 'webp',
'image/avif' => 'avif',
'image/tiff' => 'tiff',
'image/x-icon' => 'ico',
'image/svg+xml' => 'svg'
);
foreach ($matches[1] as $src) {
// 仅处理外部图片链接
if (isset($src) && strpos($src, $ecp_options) === false) {
// 获取图片 MIME 类型
$file_info = @getimagesize($src);
$mime_type = isset($file_info['mime']) ? $file_info['mime'] : 'application/octet-stream';
$file_ext = isset($ext_map[$mime_type]) ? $ext_map[$mime_type] : 'unknown';
$original_file_name = substr(md5(basename($src)), 0, 10) . current_time('YmdHis') . '.' . $file_ext;
$original_file_path = $wp_upload_dir['path'] . '/' . $original_file_name;
// 下载图片并同时使用 jsproxy 进行加速,同时以隐藏服务器真实IP
curl_setopt($ch, CURLOPT_URL, "https://jsproxy.example.com/" . $src);
$img = fopen($original_file_path, 'wb');
curl_setopt($ch, CURLOPT_FILE, $img);
curl_exec($ch);
fclose($img);
if (!file_exists($original_file_path) || filesize($original_file_path) == 0) {
$post->post_content = str_replace($src, $default_image_url, $post->post_content);
continue;
}
// 定义转换后文件名称(WebP 格式)
$webp_file_name = preg_replace('/\.[^.]+$/', '', $original_file_name) . '.webp';
$webp_file_path = $wp_upload_dir['path'] . '/' . $webp_file_name;
$image = null;
switch ($mime_type) {
case 'image/jpeg':
case 'image/jfif':
if (function_exists('imagecreatefromjpeg')) {
$image = imagecreatefromjpeg($original_file_path);
}
break;
case 'image/png':
if (function_exists('imagecreatefrompng')) {
$image = imagecreatefrompng($original_file_path);
}
break;
case 'image/bmp':
if (function_exists('imagecreatefrombmp')) {
$image = imagecreatefrombmp($original_file_path);
}
break;
case 'image/gif':
if (function_exists('imagecreatefromgif')) {
$image = imagecreatefromgif($original_file_path);
}
break;
case 'image/avif':
// 优先使用 Imagick 处理 AVIF
if (class_exists('Imagick')) {
$image = new Imagick($original_file_path);
$image->setImageFormat('webp');
$image->writeImage($webp_file_path);
$image->clear();
$image->destroy();
$image = null; // 已经转换,所以后续无需 GD 再转换
} elseif (function_exists('exec')) {
exec("ffmpeg -i " . escapeshellarg($original_file_path) . " -q:v 100 " . escapeshellarg($webp_file_path));
}
break;
case 'image/tiff':
if (class_exists('Imagick')) {
$image = new Imagick($original_file_path);
$image->setImageFormat('webp');
$image->writeImage($webp_file_path);
$image->clear();
$image->destroy();
$image = null;
}
break;
case 'image/x-icon':
// ICO:使用 imagecreatefromstring 读取文件内容
$data = file_get_contents($original_file_path);
$image = imagecreatefromstring($data);
break;
case 'image/svg+xml':
// 使用 rsvg-convert 命令将 SVG 转换为 WebP(请确保服务器上安装了 rsvg-convert)
if (function_exists('exec')) {
exec("rsvg-convert " . escapeshellarg($original_file_path) . " -f webp -o " . escapeshellarg($webp_file_path));
}
break;
default:
// 其它格式尝试使用 imagecreatefromstring 做通用处理
$data = file_get_contents($original_file_path);
$image = imagecreatefromstring($data);
break;
}
// 若通过 GD 库加载,执行转换
if ($image && function_exists('imagewebp')) {
imagewebp($image, $webp_file_path, 100); // 100表示质量值,可根据需求调整
imagedestroy($image);
unlink($original_file_path);
}
if (!file_exists($webp_file_path)) {
$post->post_content = str_replace($src, $default_image_url, $post->post_content);
continue;
}
// 替换文章内容中的图片 URL 为转换后的 WebP 图片 URL
$post->post_content = str_replace($src, $wp_upload_dir['url'] . '/' . basename($webp_file_path), $post->post_content);
$attachment = ecp_get_attachment_post(basename($webp_file_path), $wp_upload_dir['url'] . '/' . basename($webp_file_path));
$attach_id = wp_insert_attachment($attachment, ltrim($wp_upload_dir['subdir'] . '/' . basename($webp_file_path), '/'), 0);
$attach_data = wp_generate_attachment_metadata($attach_id, $webp_file_path);
wp_update_attachment_metadata($attach_id, $attach_data);
}
}
curl_close($ch);
$wpdb->update($wpdb->posts, array('post_content' => $post->post_content), array('ID' => $post->ID));
// 刷新 Redis 对象缓存,以避免不立即生效
wp_cache_delete($post_id, 'posts');
wp_cache_delete($post_id, 'post_meta');
clean_post_cache($post_id);
}
}
}
function ecp_get_attachment_post($filename, $url) {
return array(
'guid' => $url,
'post_type' => 'attachment',
'post_mime_type' => 'image/webp',
'post_title' => preg_replace('/\.[^.]+$/', '', $filename),
'post_content' => '',
'post_status' => 'inherit'
);
}
add_action('save_post', 'ecp_save_post', 120, 2);
以上代码功能:
- 自动将外链图像下载至本地
- 将下载后的外链图像转换为WebP格式
- 若图像处理失败,则显示默认图像
- 将处理后的文件重命名为随机字符串
- 在执行完成后刷新posts、post_meta对象缓存
- 使用jsproxy加速下载,同时以隐藏服务器IP地址
PS:以上代码请根据需求自行更改
使用方法
将上述代码复制至 WordPress 的 Functions 中,即可实现相应功能,具体方式在这里不过多赘述,以后单独写一篇文章详细讲解
参考
- https://akau.cn/article/8197.html
- E1245AM 提供部分技术支持
没有回复内容