在 driver
為 local
時,Storage::append()
在高并發下,會存在丟失數據問題,文件被覆寫,而非尾部添加,如果明確是本地文件操作,像日志寫入,建議使用 Illuminate\Filesystem\Filesystem
或者php原生方法file_put_content()
,通過 storage_path()
app_path()
public_path()
等方法也能達到類似效果。
源碼 分析:
Storage::append()
在driver
為local
時,調用的是vendor/laravel/framework/src/Illuminate/Filesystem/FilesystemAdapter.php
提供的方法,那么其中定義的append()
是這樣的:
/*** Append to a file.** @param string $path* @param string $data* @param string $separator* @return int*/
public function append($path, $data, $separator = PHP_EOL)
{if ($this->exists($path)) {return $this->put($path, $this->get($path).$separator.$data);}return $this->put($path, $data);
}
可以明顯的看出這里的 append
方法是將文件讀出來拼接再寫入的,那么當文件非常大時,io 消耗和內存消耗肯定會存在問題
- 那么看看
Filesystem
的append
/*** Append to a file.** @param string $path* @param string $data* @return int*/
public function append($path, $data)
{return file_put_contents($path, $data, FILE_APPEND);
}
可以看到這里是調用的 php
原生方法 file_put_contents()
,那么為什么這個原生方法不會有問題?
file_put_contents()
的實現
php
官方文檔在介紹 file_put_contents()
時是這樣描述的:
可以看到,它也并沒有調用 php 在應對高并發時的應對函數 flock()
那么當使用 FILE_APPEND
模式時,php
是如何操作的,我們注意到在描述 fwirte()
時,特別標注了 可安全用于二進制對象
那么它的含義是什么?
看到這里,也明白了 fwrite
是能保證在追加模式下的多進程調用都寫入到文件尾部
總結:php_put_contents
在使用追加模式時,其實是fwrite()
函數在追加模式下能保證文件安全的都實現內容追加到尾部,php
的 fwrite
調用的是系統的 write()
并使用參數 O_APPEND