Ruby
Python
Perl
SSH
sftp

SFTPで大きいファイルをダウンロードする (Perl/Ruby/Python)

URL

基本
https://qiita.com/cielavenir/items/9d068c49186060a47650

Pythonの.ssh/config読み出し
https://qiita.com/cielavenir/items/6aa9e6dc1166ae947c6f

大きいファイルの取得
https://qiita.com/cielavenir/items/f38223c156e4aaab58ad

そこそこ大きい100MBほどの、リモートのテキストファイルを処理したいことはあると思います。

このような場合、逐次読み出しだと遅くなるので、一気に読み出してオンメモリで処理する必要があります。


Perl

Perlはバッファまわりが賢いようなのでlocal $/でも行けますし、string ioのようなことをしても良いです。

#!/usr/bin/perl

use strict;
use Net::SFTP::Foreign;
my $sftp = Net::SFTP::Foreign->new('host');
my $file_content;
if(0){
open(my $fh,'>',\$file_content);
$sftp->get('/foo/bar',$fh);
$fh->close;
}else{
my $fh = $sftp->open('/foo/bar',Net::SFTP::Foreign::Constants::SSH2_FXF_READ);
$file_content = do { local $/; <$fh> };
$fh->close;
}
print length($file_content);
$sftp->disconnect;


Ruby

https://qiita.com/yohm/items/c469671d866ca32643f9 で触れられているように、sftp.download!は遅いです。後述するPythonと異なり、(バッファの設定とか調べましたが)SFTPの範囲ではどうしようもないようです。

身も蓋もないですが、sftp.session.scp.download!を使います。もちろん(Net::SSHのインスタンスをsshとしたとき)ssh.scp.download!でもOKです。

require 'net/sftp'

require 'net/scp'
Net::SFTP.start('host',nil){|sftp|
io = StringIO.new
sftp.session.scp.download!('/foo/bar',io)
p io.string.size
}


Python

Python2では以下のようにprefetchで良いようです。

with sftp.file('/foo/bar','r') as f:

f.prefetch()
f.read()

ただ、Python3やPyPyではあまり高速になりませんでした。getfoを使えば良いようです。

Python2/3対応にするには以下のようなことをすれば良いです。

if sys.version_info[0]>=3:

from io import StringIO, BytesIO
def getTextString(sftp, path):
io=BytesIO()
sftp.getfo(path,io)
return io.getvalue().decode('utf-8')
def getTextIO(sftp, path):
return StringIO(getTextString(sftp, path))
else:
from StringIO import StringIO
def getTextString(sftp, path):
return getTextIO(sftp, path).getvalue()
def getTextIO(sftp, path):
io=StringIO()
sftp.getfo(path,io)
io.seek(0)
return io