浅谈PHP中打开文件(fopen)的一些坑
2016-11-23
准备自己实现一个基于文件的简单缓存类,用于一些小外包项目。原本只是打算按照特定的JSON格式进行存储,然后用很方便的两个函数 file_get_content
和 file_put_content
进行读写
后来想给缓存类加个自增/减的方法,用来做简单的统计之类。如果考虑到瞬间并发情况,为了防止两个请求互相干扰,就必须得上 文件锁 了
首先是 设置缓存,这个没啥难度:
- 首先一个
$h = fopen($path, 'w+b')
打开文件 - 然后
flock($h, LOCK_EX)
锁定它,防止两次并发请求同时读写 - 接着
fwrite($h, 'xxx...')
写入内容 - 继续
flock($h, LOCK_UN)
解锁文件 - 最后
fclose($h)
关闭文件指针
然后是 读取缓存,按照上面流程如法炮制。只是把第3步的 fwrite
换成 fread
,把读出来的内容解析一下返回。看起来好像没什么问题。
然而我无论怎么试,缓存总是能正常写入,却无法读取
这我就懵逼了。经过一阵调试,发现 fread
总是返回空,这是为毛!?
再一阵调试过后,我发现在 fopen
之后,文件就已经变成空了。这就很奇怪了
我打开官方文档,仔细地再次阅读了w+
模式的说明
读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
沃日感情你所谓的 将文件大小截为零 就是会清空文件内容的意思啊?这表达得也太隐晦了吧?
看来w+是不能用了。我试着把fopen的模式切换到 r+
、x+
、a+
以及 c+
,都无法达到 先读取文件内容,然后覆盖写入 的效果
中间折腾过程略……
后来发现一个函数 ftruncate
,文档中描述为 将文件截断到给定的长度。我用 r+b
打开、锁定文件,读取内容后用该函数将其清空;递增/减数值后写入,解锁。看起来一切都那么顺利
然而我最终保存的文件,在正常内容前有一串奇怪的空字符,导致再次读取时格式不对
怀疑是清空内容后没有将指针复位。找到一个 rewind
函数,执行后再进行写入操作。最终一切正常,可喜可贺、可喜可贺!
学习了
不错 谢谢 博主www.1688616.com
程序员棒棒的
所以菊苣你的Pixiv挂件是不是得更新了 _(:з」∠)_考虑一下上HTTPS呗
Pixiv挂件没啥问题呀,干嘛要更新,还是说你有什么想要的功能?至于HTTPS,我穷,免费的国内CDN不支持HTTPS,所以我也没法部署
VeryCloud可以,每个月免费50G流量
还不够我用几天的
博主是程序员的啊?
是啊,看不出来吗?
看出来了,哈哈
file_put_content 第三个参数 不是有锁定选项吗?会有什么问题吗?
举个例子,有个缓存的值为0,通过一个接口我们可以递增这个缓存值。此时请求1开始file_get_content,然后值+1,正准备file_put_content时,另外一个并发请求2也开始file_get_content了,此时请求2获取到的值为0。也就导致请求2最终把缓存值设为1了。所以要做递增的话,必须保证从读取到写入期间一直独占锁定缓存文件