學習PHP中Fileinfo擴展的使用

今天來學習的這個擴展其實現在也已經是標配的一個擴展了,爲什麼呢?因爲 Laravel 框架在安裝的時候它就是必須的一個擴展,沒有打開它的話,連 Laravel 框架都是無法使用的。

Fileinfo 介紹

Fileinfo 是通過給定的 magic 字節序列庫來獲得文件的內容類型及編碼。它所獲取的序列庫是根據操作系統來定的,比如在 Linux 系統中默認使用的就是 /usr/share/misc/magic 這個文件。其實我們就是可以用這個擴展的函數來獲得文件的 MIME 信息,就像我們常見的 image/png 、 text/html 這些內容。

Fileinfo 這個擴展也是爲了兼容新老開發模式,所以提供了面向過程和麪向對象的兩種形式,我們先來看看面向對象的形式如何使用。

面向對象使用

$finfo = new finfo(FILEINFO_MIME);

echo $finfo->file("./1.PHP中的日期相關函數(三).php"), PHP_EOL;
// text/x-php; charset=us-ascii


echo $finfo->buffer(file_get_contents("https://www.baidu.com")) . "\n";
// text/html; charset=utf-8

$finfo->set_flags(FILEINFO_EXTENSION);
echo $finfo->file('timg.jpeg') . "\n";
// jpeg/jpg/jpe/jfif

首先,我們通過 new 一個 finfo 類來獲得文件操作對象,參數中的常量是可選的,默認情況下是 FILEINFO_NONE ,代表無特殊處理,這裏我們使用的是 FILEINFO_MIME 表明按照 RFC2045 定義的格式返回文件 mime 類型和編碼。

然後使用 file() 方法,就可以獲得指定文件的 mime 信息了。buffer() 方法則是返回字符串內容的信息,比如我們獲取一個網頁信息的內容,就能夠得到它的字符串代表的文件編碼格式爲 text/hmtl 。set_flags() 方法是可以在實例化對象之後修改它的構造參數屬性,也就是我們在實例化時設置的那個參數信息,在這裏我們將它修改爲 FILEINFO_EXTENSION ,也就是讓 finfo 對象返回文件可能的擴展名。我們使用一張圖片進行測試,返回的可能擴展名就包括註釋中展示的這些。

面向過程

針對上面面向對象的代碼,我們也展示一下相同的操作使用面向過程的函數是如何運行的。

$finfo = finfo_open(FILEINFO_MIME);
echo finfo_file($finfo,"./1.PHP中的日期相關函數(三).php"), PHP_EOL;
// text/x-php; charset=us-ascii

echo finfo_buffer($finfo, file_get_contents("https://www.baidu.com")), PHP_EOL;
// text/html; charset=utf-8

finfo_set_flags($finfo, FILEINFO_EXTENSION);
echo finfo_file($finfo, 'timg.jpeg') . "\n";
// jpeg/jpg/jpe/jfif

finfo_close($finfo);

可以看出,這裏就是將 finfo 對象換成了 finfo_open() 方法來獲得一個 finfo 操作句柄。之後使用類似的 finfo_file() 、 finfo_buffer() 、 finfo_set_flags() 函數來進行操作,實現的效果和上面的面向對象的結果是一樣的。

需要注意的,面向過程的寫法是有一個 finfo_close() 方法的,一般對於句柄類型的操作都會有一個關閉的函數來釋放句柄資源。就像 mysqli 之類的擴展一樣,finfo 中也是包含一個這樣的函數的,並且只提供面向過程的這個函數,上面的 finfo 類中是沒有這樣一個 close() 方法的。

快速返回 mime

當然,Fileinfo 擴展也爲我們提供了一個快速地返回文件 mime 信息的函數。我們可以不用使用 finfo 對象或者打開一個 finfo 句柄就可以方便快捷地獲得一個文件的 mime 信息。

echo mime_content_type('./1.PHP中的日期相關函數(三).php'), PHP_EOL;
// text/x-php

echo mime_content_type('./timg.jpeg'), PHP_EOL;
// image/jpeg

不過 PHP 官方似乎廢棄過這個函數,但現在又恢復了它,也就是說並不是特別的推薦使用這個函數。在正式的開發過程中,我們還是不要嫌麻煩,使用 finfo 對象或者 finfo 相關的函數來獲得 mime 信息會更靠譜一些。

如果我們確定要判斷的文件只是圖片類型的話,那麼我們還可以用另外一個函數來進行圖片文件的 mime 獲取。

$image = exif_imagetype("./timg.jpeg"); 
echo image_type_to_mime_type($image), PHP_EOL;
// image/jpeg

總結

非常簡單但是很實用的函數,爲什麼說它實用呢?上傳文件的安全性問題就可以靠它來解決。我們在上傳文件的時候,通常會判斷文件的後綴名及上傳數組中的文件 MIME 類型。不過很多工具是可以在上傳過程中修改文件的 MIME 類型的,也就是通過一些抓包工具修改 Content-Type 。而通過 Fileinfo 擴展獲得必須是本地或遠程已經存在的文件,也就是說不會有上傳過程中因爲修改傳輸信息而產生的安全檢查繞過問題。

因此,在 Lavarl 框架中,vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php 類中的 mimeType() 方法使用的正是 finfo_file() 這個函數來獲取文件的 mime 信息。在它的上傳組件中,Laravel 的底層 symfony 框架中,對於上傳文件的 MIME 判斷也是使用的 finfo_file() 函數,(vendor/symfony/mime/FileinfoMimeTypeGuesser.php) 並沒有直接使用正常的上傳後的 $_FILES 中的 type 字段。

測試代碼:

https://github.com/zhangyue0503/dev-blog/blob/master/php/202010/source/5.學習PHP中Fileinfo擴展的使用.php

參考文檔:

https://www.php.net/manual/zh/book.fileinfo.php

===========

各自媒體平臺均可搜索【硬核項目經理】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章