昨晚收到留言说那个缩略图插件无法下载,然后测试看了下,居然原来的附件都无法下载了,研究了下,终于发现问题所在。 先说一下 Typecho 附件的数据库设计:

通过查看 TE 的源码发现,TE 的附件信息是以以下方式存放在 contents 表中的:

 1    /*
 2        /var/Widget/Upload.php
 3        line:120
 4    */
 5    $result = array(
 6        'name' => $file['name'],
 7        'path' => self::UPLOAD_PATH . '/' . $date->year .'/'. $date->month . '/' . $fileName,
 8        'size' => $file['size'],
 9        'type' => $ext,
10        'mime' => Typecho_Common::mimeContentType($path)
11        );
12
13    /* ..... ......  */
14
15    /*
16        /var/Widget/Upload.php
17        line:258
18    */
19    $struct = array(
20            'title'     =>  $result['name'],
21            'slug'      =>  $result['name'],
22            'type'      =>  'attachment',
23            'status'    =>  'publish',
24            'text'      =>  serialize($result),
25            'allowComment'      =>  1,
26            'allowPing'         =>  0,
27            'allowFeed'         =>  1
28        );

其中 $struct['text'] 返回的是序列化的附件信息字符串,其结构如下:

1    return "a:5:{s:4:"name";s:18:"Thumbnail-show.zip";s:4:"path";s:37:"/attachment/2012/01/4f01d62cb3bfc.zip";s:4:"size";i:8513;s:4:"type";s:3:"zip";s:4:"mime";s:15:"application/zip";}";

此字符串的结构为 "数据类型":"长度":"值" 。 注:integerdouble 直接返回 "数据类型":"值"https://mrasong.com/a/serialize-and-unserialize 关于更多的 serializeunserialize 请参考官方说明。

现在返回正题,TE 本身设计时,上传的附件是按 YYYY/mm/ 文件夹存放的,因为个人觉得没什么附件上传,所以就把 /var/Widget/Upload.php 文件中的存放路径修改为 YYYYmm/ 直接以月份存放,数据库中原来所有的 YYYY/mm/ 路径全部改为 YYYYmm/ ,ftp 上附件也迁移了。

理论上,这样是好了,对,是好了,因为新上传的文件没有任何问题,通过 http://host/usr/files/YYYYmm/xxxxxx.xxx 可以直接下载,原来上传的附件也可以通绝对路径下载,但是… 问题还是出现了,我用 Attachment 插件,无法下载。原来的附件,在附件管理中,路径全为 http://mrasong.com 。 问题就出在上面介绍的 serialize 上,serialize 返回的数据中有一个长度,也就是说,改过数据库中附件的 path 但是没有改 path 字符串的长度。

1将    s:38:"/usr/files/201201/4f01d62cb3bfc.zip"
2改为  s:37:"/usr/files/201201/4f01d62cb3bfc.zip"

因为少了一个 "/" 字符串的长度少了 1,到此,问题终于解决了。 总结: Typecho 确实写得非常好,通过研究 TE 的源码,自己也学了不少。顺便感谢下 Mr.Jiang 同学的提醒。