如何使用Eloquent和PostgreSQL将IP地址保存为二进制?
如何使用Eloquent和PostgreSQL将IP地址保存为二进制?
首先,这是我获取信息的SO问题+答案链接 - laravel 4 saving ip address to model。
因此,我的表可能会有数百万行,为了保持存储空间低,我选择了第二个选项 - 使用Schema构建器的binary()列,并利用Eloquents的访问器/修改器将IP转换/存储为二进制。
这是我的表格:
Schema::create('logs', function ( Blueprint $table ) { $table->increments('id'); $table->binary('ip_address'); // postgresql将此列报告为BYTEA $table->text('route'); $table->text('user_agent'); $table->timestamp('created_at'); });
我遇到的第一个问题是保存IP地址。我在我的模型上设置了一个访问器/修改器,使用inet_pton()
和inet_ntop()
将IP字符串转换为二进制。示例:
public function getIpAddressAttribute( $ip ) { return inet_ntop( $ip ); } public function setIpAddressAttribute( $ip ) { $this->attributes['ip_address'] = inet_pton( $ip ); }
尝试保存IP地址导致整个请求失败 - nginx只会返回502错误。
好吧。所以我想应该是Eloquent/PostgreSQL在传递二进制数据时出了问题。
我搜索了一下,找到了pg_escape_bytea()
和pg_unescape_bytea()
函数。我更新了我的模型如下:
public function getIpAddressAttribute( $ip ) { return inet_ntop(pg_unescape_bytea( $ip )); } public function setIpAddressAttribute( $ip ) { $this->attributes['ip_address'] = pg_escape_bytea(inet_pton( $ip )); }
现在,我可以顺利保存IP地址(至少没有抛出任何错误)。
我遇到的新问题是当我尝试检索和显示IP时。pg_unescape_bytea()
报错: pg_unescape_bytea() expects parameter 1 to be string, resource given
。
奇怪。所以我使用dd()
在访问器中打印$ip,结果是resource(4, stream)
。这是预期的吗?还是Eloquent在处理列类型时遇到了问题?
我继续搜索,发现可能是pg_unescape_bytea()
没有正确地取消转义数据 - https://bugs.php.net/bug.php?id=45964。
经过多次头撞和抓头发,明显我可能从错误的方向解决这个问题,需要一些新的视角。
那么,我做错了什么?我应该使用Postgres的BIT VARYING
而不是BYTEA
通过修改列类型来解决吗 -
DB::statement("ALTER TABLE logs ALTER COLUMN ip_address TYPE BIT VARYING(16) USING CAST(ip_address AS BIT VARYING(16))");`
- 还是我仅仅误用了pg_escape_bytea / pg_unescape_bytea
?
非常感谢任何帮助!
在评论中已经提到过的,你应该使用相应的PostgreSQL数据类型,在你的特定情况下,处理会更容易。与MySQL相比,PostgreSQL有很多其他类型(如JSON),可以参考PostgreSQL数据类型概述页面。
也就是说,其他人可能会遇到类似的bytea
字段的问题。你得到Resource
而不是string
的原因是,PostgreSQL将bytea
字段视为流。一个非常天真的方法是先获取流,然后返回数据:
public function getDataAttribute($value) { // This will kill your server under high load with large values. $data = fgets($value); return pg_unescape_bytea($data); }
你可以想象,在多个人尝试获取大文件(目前是几百 MiB 或几个 GiB)的情况下,使用大量内存的大型数据对象会在服务器上造成很大的压力(在没有交换空间的移动设备上甚至可能成为问题)。在这种情况下,你应该在服务器和客户端上使用流,并且只在客户端获取你真正需要的数据。