如果mime类型检测失败,是否有安全可靠的方法来检查文件是否为文本文件?

时间:2017-04-12 10:27:56

标签: php mime-types

我有一个网站,用于处理用户上传的文本文件,以确保它们实际上是文本文件我在PHP中检查mime类型,如下所示:

$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $filepath);
finfo_close($finfo);

大部分时间都可以正常工作。问题是有时上传的文件包含一些控制字符(不可打印的字符,如nul或stx)。尝试获取这些文件的mime类型总是返回application / octet-stream。例如,我有一个560行的文本文件,在第12行包含一个空字符,因此被识别为application / octet-stream

当检测到mime类型不起作用时,是否有任何安全可靠的方法来检查上传的文件是否为文本文件?

1 个答案:

答案 0 :(得分:0)

原来php中的大多数文件读取函数都是binary safe,这就解决了我如何安全地读取文件的问题。

我最终通过计算控制字符解决了我的问题。如果文件的块有超过1%的控制字符,我认为它不是文本文件。

以下功能适用于我正在使用的功能(即使它仅适用于UTF-8文件)

public static function isTextFile($filepath)
{
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mimeType = finfo_file($finfo, $filepath);
    finfo_close($finfo);

    if(substr($mimeType, 0, 5) === "text/") {
        return true;
    }

    if($mimeType !== "application/octet-stream") {
        return false;
    }

    $handle = fopen($filepath, 'rb');

    while (!feof($handle)) {
        $chunk = fread($handle, 4096);
        $controlCharCount = 0;

        if(($length = strlen($chunk)) === 0) {
            continue;
        }

        for($i = 0; $i < $length; $i++) {
            if($chunk[$i] !== "\r" && $chunk[$i] !== "\n" && ctype_cntrl($chunk[$i])) {
                $controlCharCount++;
            }
        }


        if(100 - $controlCharCount / $length * 100 < 99.0) {
            return false;
        }
    }

    fclose($handle);

    return true;
}