昨天调一个上传接口,明明传的是2MB的小图,filesize()却返回0,还把用户挡在了大小校验外面。查了半天发现是用了缓存文件状态的老写法,PHP把上一次的stat结果给复用了。filesize这个函数看着简单,坑基本都集中在缓存和单位上。
它返回的是字节数,不是KB
filesize($path)返回文件的字节大小,单位是byte。想知道多少KB要自己除1024,多少MB再除一次1024。它内部走的是stat系统调用,拿的是inode里记的大小,跟文件内容有没有读完无关,所以对一个正在写入的文件取大小,可能得到一个中间值。
clearstatcache 是绕不开的一步
PHP为了性能会把stat结果缓存起来。你在同一个请求里先filesize、再追加写文件、又filesize,第二次拿到的还是旧数字。解决办法是在第二次取值前调用clearstatcache(true, $path),只清这一个文件;不带参数则清全部缓存。长驻脚本比如Swoole、队列worker里尤其要注意,不然会一直读老值。
$size = filesize($file); if ($size === false) { echo 文件不可读; } clearstatcache(true, $file);返回值可能是false,别直接比较
文件不存在、权限不足、路径是目录时,filesize返回false而不是抛异常。直接写 if (filesize($f) > 1048576) 会把false当0处理,恰好绕过限制。稳妥的写法是先file_exists加is_file判断,或者用 @filesize 后接 === false 判空。
超大文件与远程地址
32位PHP上,超过2GB的文件,filesize会返回负数或溢出,得换成用shell_exec调stat命令,或者升级到64位版本。另外filesize不支持http地址,给URL会直接警告,远程文件得先下载到本地,或者读get_headers返回的Content-Length。
- 要精确:清缓存后再取值,并且判断返回值是否为false。
- 要省事:确定文件不在变化,单个请求里取一次就够。
- 传大文件:优先读$_FILES的size字段,那是上传时PHP已经算好的。