wordpress是用php语言开发的博客平台,它扩展性强,容易扩展,很适合拿来做二次开发。
1,问题由来
本周五,我在浏览公司的网站(基于wordpress开发)时发现,网站首页上有两篇文章的缩略图重复了,于是我进入网站后台检查,想看下是不是某位员工在撰写文章时不小心这两篇文章选择了相同的图片作为封面图片。
结果发现这两篇文章选用的封面图片的文件名还真是相同,但我转念又想到,员工再糊涂也不至于犯这样的错误。于是,我检查了下这几篇文章的封面图片url地址,这些地址是相同的。
我想到,有可能是这两张图片的文件名相同(至于为什么会出现这种情况,恐怕只有鬼知道),然后文件被上传后,后上传的图片将前面上传的图片覆盖,最后只有一张图片留在了云存储的服务器上。
注:我写了个插件,在上传图片时,可以将图片上传至百度云的BOS上。
2,问题该如何解决
其实,一开始我是很纠结这个问题的:是让编辑文章的员工下次注意,每次上传图片时都要按年月日时间、加序号和图片其它信息将图片重名后再上传呢,还是自己默默的在自己写的插件里加上一些代码把图片重命名呢?
好吧,写到这里,你肯定能猜到我做了什么样的选择?写代码吧。
3,曲折的过程
我的插件原来只负责把图片上传到百度云的BOS上,然后把本地图片删除,主要用到了wordpress的wp_update_attachment_metadata钩子。我在这个钩子里调用了一个自己定义的函数upload_attachement_to_bos,
这个函数就负责把图片上传到BOS并删除本地图片。一开始,我一直在想如何在这个函数里做文章:把原图重命名,把缩略图重命名,额,试了半天都没有效果。我检查了下BOS上的图片,上传成功、文件名也是修改后的。
可是为什么网站的图片url地址还是没变?
不能着急,先去媒体库下面看下图片,结果发现媒体库的图片文件名、标题、url中的名称都还是原来的文件名。
我虽然把服务器上的文件名都改了,但数据库中与图片有关的字段的值都没有改。于是,我尝试更改wordpress的数据表,来看媒体库和文章缩略图中的图片文件名是否有变化。
期间,我试着修改post表中的postname、post_title、guid字段,然而并没有用。postname:文章的别名,显示在url地址中,一般可用来美化url地址,post_title:与媒体库中,附件详情表单页面中的图片标题对应,就相当于文章标题,
guid:这个,说实话,我也不知道它有什么用,好像是文章的唯一标识。
我最后定位到postmeta表中的meta_key: _wp_attached_file对应的meta_value。这个_wp_attached_file的值会影响文章附件的相关信息,如媒体库中的附件详情页中的图片url、文件名都是取的meta_value值
另外就是wordpress主题(据我测试的有限的几个主题)下首页文章缩略图、文章单页中的头图url中的文件名都是取的这个值。
好,总算找到病根了,剩下的问题就是如何把这个值给修改掉。
4,问题暂时解决,但还有一些遗留问题
前面说到了,我用到了wp_update_attachment_metadata钩子,于是我在对应的钩子函数里做文件名修改、更新_wp_attached_file的工作。钩子函数代码大致如下:
1 // 钩子函数: 重命名文件,更新文件meta信息,调用上传函数,并将上传的原图在bucket下的路径信息保存到数据库 2 function update_attachment_metadata($data, $post_id) { 3 /* 重命名文件防止冲突 */ 4 date_default_timezone_set('PRC'); 5 $wp_upload_dir = wp_upload_dir(); 6 $old_path = $wp_upload_dir['basedir'] . '/' . $data['file']; 7 $ext = pathinfo($old_path, PATHINFO_EXTENSION); 8 $old_namestr = str_replace('.' . $ext, '', basename($data['file'])); 9 $new_namestr = date('YmdHis-') . dechex(mt_rand(100000, 999999)); 10 $new_path = $wp_upload_dir['path'] . '/' . $new_namestr. '.' . $ext; 11 12 rename($old_path, $new_path); 13 14 if (isset($data['sizes']) && count($data['sizes']) > 0) { 15 $thumb_data = &$data['sizes']; 16 foreach ($thumb_data as $key => $thumb) { 17 $old_thumbpath = $wp_upload_dir['basedir'] . '/' . substr($data['file'], 0, 8) 18 . $thumb['file']; 19 $new_thumbpath = str_replace($old_namestr, $new_namestr, $old_thumbpath); 20 if (file_exists($old_thumbpath)) { 21 rename($old_thumbpath, $new_thumbpath); 22 } 23 } 24 } 25 /* 更新data中的文件名 */ 26 $old_jsdata = json_encode($data, JSON_UNESCAPED_UNICODE); 27 $new_data = json_decode(str_replace($old_namestr, $new_namestr, $old_jsdata), true); 28 29 unset($data, $old_jsdata); 30 31 $ori_object_key = upload_attachement_to_bos($new_data, $post_id); 32 // 将原始图片在BOS bucket下的路径信息(object信息)添加到数据库 33 add_post_meta($post_id, 'bos_info', $ori_object_key); 34 35 /* 更新数据库中postmeta表中_wp_attached_fies的值 */ 36 $old_meta = get_post_meta($post_id, '_wp_attached_file', true); 37 update_post_meta($post_id, '_wp_attached_file', str_replace($old_namestr, $new_namestr, $old_meta)); 38 39 return $new_data; 40 }
关键是36、37行修改postmeta表中的_wp_attached_file值,然后需要注意的是,我这里对函数的返回值进行了处理,返回的$new_data数组是替换文件名后的数组,我暂时还不知道这么做会不会产生什么副作用。
然后,我把插件代码更新了下。嗯,插件起作用了,重命名功能实现,原有功能没被破坏。
5,其他啰哩啰嗦的问题
(1)代码中的$data数组是一个多维数组,多维数组做字符串替换该怎么做呢?str_replace()函数好像只能替换一维数组。我在代码里用的是将数组转化为字符串再替换的方法,用到了json_encode()和json_decode(),不知道还有没有其他更好的方法。
(2)文件重名的的方法rename(),在对文件重命名之前要先判断文件是否存在和文件夹权限(或者权限不够时,修改文件夹权限),我这里没有做。
ps:我写的这个wp-bos插件(支持wordpress使用云存储作为图片的存储空间,目前支持BOS百度云存储)托管在gitoschina和github上,
不过托管的代码暂时还没有将重命名的功能加入,等到插件在网站上运行一段时间没有问题后,我会及时将更改push上去,感兴趣的朋友可以关注下。