我有一个访问 PostgreSQL 数据库的应用程序,需要根据一些需要的处理从中读取一些大型二进制数据。这可能是数百 MB 甚至几 GB 的数据。请不要讨论有关使用文件系统的讨论,这就是现在的方式。
该数据只是各种类型的文件,例如它可能是一个 Zip 容器或某种其他类型的存档。一些需要的处理是列出 Zip 的内容,甚至可能提取一些成员进行进一步处理,可能对存储的数据进行哈希处理......最后数据被读取多次,但只写入一次来存储它。
我使用的所有 Perl 库都能够使用文件句柄,一些使用IO::Handle
,另一些使用IO::String
or IO::Scalar
,还有一些仅使用低级文件句柄。所以我所做的是创建一个子类,IO::Handle
它IO::Seekable
的作用类似于DBD::Pg
. 在 CTOR 中,我创建了一个到数据库的连接,打开一些提供的 LOID 用于读取并将 Postgres 提供的句柄存储在实例中。然后将我自己的句柄对象转发给能够使用此类文件句柄并可以直接在 Postgres 提供的 blob 中读取和查找的任何人。
问题是使用低级文件句柄或低级文件句柄操作的库IO::Handle
。Digest::MD5
似乎是一个,Archive::Zip
另一个。Digest::MD5
croak
s 并告诉我没有提供句柄,Archive::Zip
另一方面尝试从我的创建一个新的自己的句柄,IO::Handle::fdopen
在我的情况下调用并失败。
sub fdopen {
@_ == 3 or croak 'usage: $io->fdopen(FD, MODE)';
my ($io, $fd, $mode) = @_;
local(*GLOB);
if (ref($fd) && "".$fd =~ /GLOB\(/o) {
# It's a glob reference; Alias it as we cannot get name of anon GLOBs
my $n = qualify(*GLOB);
*GLOB = *{*$fd};
$fd = $n;
} elsif ($fd =~ m#^\d+$#) {
# It's an FD number; prefix with "=".
$fd = "=$fd";
}
open($io, _open_mode_string($mode) . '&' . $fd)
? $io : undef;
}
我想问题是句柄的低级副本,它删除了我自己的实例,因此不再有实例具有我的数据库连接和所有这些东西。
那么,在我的情况下,是否有可能提供一些IO::Handle
可以成功用于需要低级文件句柄的地方?
我的意思是,我没有真正的文件句柄,我只有一个对象,其中方法调用被包装到它们相应的 Postgres 方法中,为此需要数据库句柄等。所有这些数据都需要存储在某个地方,需要完成包装等。
我试图做其他人正在做的事情,例如IO::String
,它还使用tie
了例如。但最终那个用例是不同的,因为 Perl 能够自己为某些内部存储器创建一个真正的低级文件句柄。在我的情况下根本不支持的东西。我需要保留我的实例,因为只有它知道数据库的句柄等。
IO::Handle
像通过调用方法一样使用我的句柄read
,这样的工作就像预期的那样,但我想更进一步,并且更兼容那些不希望在IO::Handle
对象上工作的人。很像IO::String
或File::Temp
可以用作低级文件句柄。
package ReadingHandle;
use strict;
use warnings;
use 5.10.1;
use base 'IO::Handle', 'IO::Seekable';
use Carp ();
sub new
{
my $invocant = shift || Carp::croak('No invocant given.');
my $db = shift || Carp::croak('No database connection given.');
my $loid = shift // Carp::croak('No LOID given.');
my $dbHandle = $db->_getHandle();
my $self = $invocant->SUPER::new();
*$self->{'dbHandle'} = $dbHandle;
*$self->{'loid'} = $loid;
my $loidFd = $dbHandle->pg_lo_open($loid, $dbHandle->{pg_INV_READ});
*$self->{'loidFd'} = $loidFd;
if (!defined($loidFd))
{
Carp::croak("The provided LOID couldn't be opened.");
}
return $self;
}
sub DESTROY
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
$self->close();
}
sub _getDbHandle
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
return *$self->{'dbHandle'};
}
sub _getLoid
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
return *$self->{'loid'};
}
sub _getLoidFd
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
return *$self->{'loidFd'};
}
sub binmode
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
return 1;
}
sub close
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
my $dbHandle = $self->_getDbHandle();
my $loidFd = $self->_getLoidFd();
return $dbHandle->pg_lo_close($loidFd);
}
sub opened
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
my $loidFd = $self->_getLoidFd();
return defined($loidFd) ? 1 : 0;
}
sub read
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
my $buffer =\shift // Carp::croak('No buffer given.');
my $length = shift // Carp::croak('No amount of bytes to read given.');
my $offset = shift || 0;
if ($offset > 0)
{
Carp::croak('Using an offset is not supported.');
}
my $dbHandle = $self->_getDbHandle();
my $loidFd = $self->_getLoidFd();
return $dbHandle->pg_lo_read($loidFd, $buffer, $length);
}
sub seek
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
my $offset = shift // Carp::croak('No offset given.');
my $whence = shift // Carp::croak('No whence given.');
if ($offset < 0)
{
Carp::croak('Using a negative offset is not supported.');
}
if ($whence != 0)
{
Carp::croak('Using a whence other than 0 is not supported.');
}
my $dbHandle = $self->_getDbHandle();
my $loidFd = $self->_getLoidFd();
my $retVal = $dbHandle->pg_lo_lseek($loidFd, $offset, $whence);
$retVal = defined($retVal) ? 1 : 0;
return $retVal;
}
sub tell
{
my $self = shift || Carp::croak('The method needs to be called with an instance.');
my $dbHandle = $self->_getDbHandle();
my $loidFd = $self->_getLoidFd();
my $retVal = $dbHandle->pg_lo_lseek($loidFd);
$retVal = defined($retVal) ? $retVal : -1;
return $retVal;
}
1;