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



首先,这里是SO问答,我在这里得到了我的信息-laravel 4将ip地址保存到模型中。

因此,我的表可能会有数百万行,因此为了保持较低的存储空间,我选择了选项2——使用Schema生成器的二进制()列,并在Eloquents的访问器/赋值器的帮助下将IP转换/存储为二进制。

这是我的桌子:

Schema::create('logs', function ( Blueprint $table ) {
    $table->increments('id');
    $table->binary('ip_address'); // postgresql reports this column as 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)的问题,而大数据对象在服务器上需要大量内存(这甚至可能在没有交换的移动设备上出现问题)。在这种情况下,您应该在服务器和客户端上处理流,只在客户端上获取您真正需要的数据。

最新更新