最近写的文章几乎都是纯文字的(其实是懒得发图)导致页面不太美观,我就在想可不可以为文章增加一个随机图片呢,想着以为很简单就搞定了,实际操作起来出现了很多问题,不过最终还是成功解决了。
一、创建图片API接口
首先要先做一个图片API接口,这个接口要满足支持本地服务器的图片,也要支持外链图片。并且增加一个随机机制,使其在被打开页面的时候,多篇文章的封面图片不会相同,刷新页面也可以随机变化,还需要增加一个缓存机制,缓存图片并尽量减少请求次数。
API文件结构:
/文件/img/ 目录下:
├─ api.php # API核心(网址/api.php即可使用,从config.php读取所有配置)
├─ config.php # 主配置(保留外部链接配置入口,关联独立文件)
├─ external-links-list.php # 单独管理外部链接(仅放具体URL,可随时修改)
└─ local-images/ # 本地图片文件夹
1.首先创建一个api.php的文件并放入以下代码
这是用来访问api的入口
[content_hide]
<?php
/**
* 随机图片API(带缓存优化版)
* 特点:1. 服务器缓存可用图片列表 2. 允许浏览器/CDN缓存API响应 3. 平衡缓存效率与随机性
*/
// 1. 优化的缓存控制(允许浏览器/CDN缓存,设置合理有效期)
// public: 允许中间缓存(如CDN)缓存
// max-age=300: 缓存5分钟(可根据需求调整,建议300-3600秒)
// must-revalidate: 缓存过期后必须重新验证,避免使用 stale 数据
header("Cache-Control: public, max-age=300, must-revalidate");
// 兼容HTTP/1.0(Pragma在HTTP/1.1中被Cache-Control替代,仅作兼容)
header("Pragma: cache");
// Vary: 告诉CDN/代理根据Accept-Encoding区分缓存(不同压缩方式的响应分开存)
header("Vary: Accept-Encoding");
// 安全头:防止浏览器嗅探错误的MIME类型
header("X-Content-Type-Options: nosniff");
// 2. 加载配置
require_once 'config.php';
// 3. 服务器端缓存可用图片列表(逻辑不变,减少IO/外部请求)
$cache_key = 'random_image_list_cache';
$cache_expire = 300; // 图片列表缓存5分钟(与API响应缓存时长保持一致,可独立调整)
$all_images = [];
// 尝试从缓存读取图片列表
if (function_exists('get_transient')) {
// WordPress环境:用transient缓存
$cached_list = get_transient($cache_key);
if ($cached_list !== false && is_array($cached_list)) {
$all_images = $cached_list;
}
} else {
// 非WP环境:用文件缓存
$cache_file = __DIR__ . '/image_list_cache.txt';
if (file_exists($cache_file) && time() - filemtime($cache_file) < $cache_expire) {
$cached_content = file_get_contents($cache_file);
$all_images = json_decode($cached_content, true);
}
}
// 缓存不存在/过期:重新收集图片列表并缓存
if (empty($all_images)) {
// 处理本地图片
if (!empty($local['folder']) && !empty($local['url_prefix']) && is_dir($local['folder'])) {
$allowed_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'];
$dir = opendir($local['folder']);
while (($file = readdir($dir)) !== false) {
if ($file == '.' || $file == '..') continue;
$file_path = $local['folder'] . $file;
if (is_file($file_path)) {
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
if (in_array($ext, $allowed_exts)) {
$url_prefix = rtrim($local['url_prefix'], '/') . '/';
$local_url = $url_prefix . rawurlencode($file);
$all_images[] = $local_url;
}
}
}
closedir($dir);
}
// 处理外部链接
if (!empty($external) && is_array($external)) {
foreach ($external as $link) {
$link = trim($link);
if (strpos($link, 'http://') === 0 || strpos($link, 'https://') === 0) {
$all_images[] = $link;
}
}
}
// 缓存新列表(非空时)
if (!empty($all_images)) {
if (function_exists('set_transient')) {
set_transient($cache_key, $all_images, $cache_expire);
} else {
$cache_file = __DIR__ . '/image_list_cache.txt';
file_put_contents($cache_file, json_encode($all_images));
}
}
}
// 检查可用图片
if (empty($all_images)) {
die("没有可用图片");
}
// 4. 随机选择图片(缓存有效期内,相同客户端会看到相同图片,符合缓存逻辑)
$total = count($all_images);
// 用微秒时间戳+客户端IP片段作为种子,确保不同客户端缓存的图片不同
$seed = (int)(microtime(true) * 1000000) + crc32($_SERVER['REMOTE_ADDR'] ?? 'unknown');
mt_srand($seed);
$random_index = mt_rand(0, $total - 1);
// 5. 跳转到随机图片
$random_image = $all_images[$random_index];
header("Location: {$random_image}", true, 302);
exit;
[/content_hide]
2.创建文件config.php并放入以下代码
这个文件是api的配置用来管理本地图片和外部的链接(需要更改url_prefix里的url)
[content_hide]
<?php
/**
* API主配置文件(保留外部链接配置入口,具体链接在external-links-list.php中)
*/
// 1. 本地图片配置(原有逻辑不变)
$local = [
'folder' => __DIR__ . '/local-images/', // 本地图片服务器绝对路径
'url_prefix' => 'https://ceshi1.tkzyz.com/wp-content/themes/b2child/img/local-images/' // 本地图片访问URL前缀
];
// 2. 外部链接配置(保留入口,具体链接从独立文件引入)
$external = []; // 初始化外部链接数组(API会读取这个数组)
$external_config_file = __DIR__ . '/external-links-list.php'; // 指向单独的链接文件
// 引入独立的外部链接列表(核心:关联external-links-list.php)
if (file_exists($external_config_file)) {
require_once $external_config_file;
// 将独立文件中的$external_links赋值给$external(确保API能识别)
if (!empty($external_links) && is_array($external_links)) {
$external = $external_links;
}
} else {
// 容错提示:若独立文件不存在,给出明确指引(避免API报错)
die("外部链接配置文件缺失:请在 " . __DIR__ . " 目录下创建 external-links-list.php 文件,并添加外部图片链接");
}
[/content_hide]
3.创建文件external-links-list.php并放入以下代码
这个文件是用来管理增加或删除外部链接的图片
[content_hide]
<?php
/**
* 外部图片链接列表(单独维护,无需改动其他文件)
* 规则:1. 每个链接占一行 2. 必须以http/https开头 3. 空行和#注释行会自动过滤
*/
// 定义外部链接数组(变量名需与config.php中一致,确保能被读取)
$external_links = [
// 已有的外部链接
'https://random-picture.aneu.cn/acg-webp/0072vf1pgy1foxkfrmrlgj31hc0u0wt1.webp',
'https://random-picture.aneu.cn/acg-webp/0072vf1pgy1foxlhvnh9qj31hc0u0tmu.webp',
// 新增外部链接直接在这里加(示例)
// 'https://picsum.photos/id/100/800/600',
// 'https://picsum.photos/id/200/800/600',
// # 这是注释行,不会被读取
// '错误链接(会被过滤)' // 没有http开头,自动排除
];
// 自动过滤无效链接(容错:避免手动添加错误导致API失效)
$external_links = array_filter($external_links, function($link) {
$link = trim($link);
// 只保留以http/https开头的有效链接
return strpos($link, 'http://') === 0 || strpos($link, 'https://') === 0;
});
[/content_hide]
4.创建文件夹local-images
这个文件夹是放入本地图片文件的,放入即可增加,删除即可减少
二、修改主题文件
目录/wp-content/themes/b2child
文件functions.php(最好修改子主题的文件防止以后升级主题要重新修改)
将以下代码放入文件functions.php的底部(本文件的其他位置也可以,记得注释防止忘记)
[content_hide]
//自动外部API封面
// 未设置封面时自动调用API图片
// B2主题默认封面替换(平衡多样性与请求量)
function b2_replace_default_thumbnail_with_random_api() {
if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
return;
}
$original_default_img = 'https://ceshi1.tkzyz.com/wp-content/themes/b2/Assets/fontend/images/default-img.jpg';
$api_base = 'https://ceshi1.tkzyz.com/wp-content/themes/b2child/img/api.php';
if (!is_singular() && !is_archive() && !is_home()) {
return;
}
ob_start(function($buffer) use ($original_default_img, $api_base) {
$pattern = '/' . preg_quote($original_default_img, '/') . '/';
// 1. 时间分段(保留:15分钟更新一次,减少请求)
$time_segment = floor(time() / 900); // 15分钟
// 2. 核心:为每个文章生成唯一标识(确保不同文章有不同图片)
// 单篇文章页:用文章ID作为唯一标识
// 列表页(首页/分类页):从HTML中提取文章ID(B2主题会在封面容器添加data-id)
$post_unique_ids = [];
if (preg_match_all('/data-post-id="(\d+)"/', $buffer, $matches)) {
$post_unique_ids = $matches[1]; // 提取页面中所有文章的ID
}
$current_post_id = is_singular() ? get_queried_object_id() : '';
// 3. 替换逻辑:结合文章ID+位置,确保每个封面唯一
$replaced_buffer = preg_replace_callback($pattern, function($matches) use (
$api_base, $time_segment, $post_unique_ids, $current_post_id
) {
// 为当前替换的封面匹配对应的文章ID(列表页用提取的ID,单页用当前文章ID)
static $id_index = 0;
$post_id = '';
if (is_singular() && $current_post_id) {
$post_id = $current_post_id; // 单篇文章页:固定用当前文章ID
} elseif (!empty($post_unique_ids[$id_index])) {
$post_id = $post_unique_ids[$id_index]; // 列表页:按顺序匹配文章ID
$id_index++;
} else {
$post_id = uniqid(); // 极端情况:无ID时用唯一随机值
}
// 生成唯一标识:文章ID+时间分段+位置(确保不同文章/位置不同)
static $position = 0;
$position++;
$unique_identifier = md5($post_id . $time_segment . $position);
$random_param = 'rand=' . substr($unique_identifier, 0, 12); // 更长的参数,减少重复
// 缓存键:基于文章ID+时间+位置(确保每个文章封面独立缓存)
$cache_key = 'b2_thumb_unique_' . $post_id . '_' . $time_segment . '_' . $position;
$cached_url = '';
if (function_exists('get_transient') && function_exists('set_transient')) {
$cached_url = get_transient($cache_key);
if (!$cached_url) {
$cached_url = $api_base . '?' . $random_param;
set_transient($cache_key, $cached_url, 900); // 15分钟缓存
}
} else {
$cached_url = $api_base . '?' . $random_param;
}
// 保留浏览器缓存优化,同时确保每个封面URL唯一
return $cached_url
. '" loading="lazy" '
. 'onerror="this.onerror=null; this.src=\'\'; this.style.display=\'none\'; this.alt=\'封面加载失败\''
. '; this.setAttribute(\'cache-control\', \'max-age=86400\')';
}, $buffer);
return $replaced_buffer;
});
}
add_action('template_redirect', 'b2_replace_default_thumbnail_with_random_api', 20);
[/content_hide]
完成以上操作即可实现不设置封面的情况下默认封面变为调用随机API封面
THE END
