如何使用Eloquent和PostgreSQL将IP地址保存为二进制?

6 浏览
0 Comments

如何使用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

非常感谢任何帮助!

0
0 Comments

在评论中已经提到过的,你应该使用相应的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)的情况下,使用大量内存的大型数据对象会在服务器上造成很大的压力(在没有交换空间的移动设备上甚至可能成为问题)。在这种情况下,你应该在服务器和客户端上使用流,并且只在客户端获取你真正需要的数据。

0