本地正常的usort图片按创建时间排序在Heroku服务器失效,求解决方案
你的问题主要来自两个核心原因:排序回调函数的逻辑错误,以及Heroku文件系统的特殊限制,下面逐一拆解并给出解决办法:
1. 先修复排序回调的逻辑错误
你当前的usort回调返回的是布尔值(true/false),但PHP的usort要求回调函数返回整数:返回-1表示$a排在$b前面,0表示两者相等,1表示$b排在$a前面。
原来的代码return filemtime($a) > filemtime($b);会把true转为1,false转为0,这在某些场景下可能凑巧工作,但无法正确处理所有排序情况(比如当$a的修改时间比$b早时,返回false即0,usort会认为两者顺序无关,导致排序混乱)。
正确的写法应该用差值法或者PHP 7+的太空船运算符:
// 升序排序(旧文件在前,新文件在后) usort($images, function($a, $b) { $timeA = filemtime($a); $timeB = filemtime($b); // 处理文件不存在/无法获取时间的异常情况 if ($timeA === false || $timeB === false) { return strcmp(basename($a), basename($b)); // fallback到按文件名排序 } return $timeA <=> $timeB; // 太空船运算符,PHP 7+支持 // 兼容PHP 5.x的写法: // return $timeA - $timeB; }); // 如果要降序,反过来即可: // return $timeB <=> $timeA;
2. 解决Heroku文件系统的限制问题
Heroku的文件系统是临时且只读的(除了/tmp目录),而且所有部署的静态文件(即你代码包里的图片)会被打包成slug,每次dyno启动时都会从slug复制文件——这意味着所有静态文件的filemtime返回的都是部署时间,而不是你本地的文件创建/修改时间。所以用filemtime排序自然会失效,因为所有文件的时间都是一样的。
根据你的图片类型,有两种解决方案:
情况A:图片是用户上传的动态文件
绝对不要把用户上传的文件存在Heroku的本地文件系统里(重启dyno就会丢失),应该改用云存储服务(比如AWS S3、Cloudinary等),同时把文件的上传时间/创建时间存在数据库中。排序时直接从数据库读取时间字段来排序,完全不需要依赖filemtime。
情况B:图片是部署时自带的静态文件
你需要在本地提前收集好每个图片的真实创建/修改时间,把这些元数据存在一个配置文件(比如JSON、CSV)里,部署时把这个配置文件一起打包到slug中。然后代码里读取这个配置文件的时间来排序,而不是调用filemtime。
举个例子:
- 本地生成
image_timestamps.json文件:
{ "sunset.jpg": 1690123456, "mountain.jpg": 1690123500, "lake.jpg": 1690123600 }
(这里的数字是Unix时间戳,你可以用本地的filemtime批量生成)
- 代码中读取这个文件来排序:
// 读取元数据文件 $imageTimestamps = json_decode(file_get_contents(__DIR__ . '/image_timestamps.json'), true); usort($images, function($a, $b) use ($imageTimestamps) { $fileNameA = basename($a); $fileNameB = basename($b); // 优先用预存的时间, fallback到filemtime(本地环境兼容) $timeA = $imageTimestamps[$fileNameA] ?? filemtime($a); $timeB = $imageTimestamps[$fileNameB] ?? filemtime($b); if ($timeA === false || $timeB === false) { return strcmp($fileNameA, $fileNameB); } return $timeA <=> $timeB; });
额外检查点
- 确认
$images数组里的文件路径在Heroku上是正确的,避免因为路径错误导致filemtime返回false。 - Heroku的
/tmp目录是唯一可写的临时目录,如果你的图片存在这里,注意dyno重启后会被清空,而且文件的mtime是上传到tmp的时间,不是原始创建时间。
内容的提问来源于stack exchange,提问作者Hclee

