19
def download():
if os.path.exists( dst_dir_path ) == False:
    logger.error( "Cannot access destination folder %s. Please check path and permissions. " % ( dst_dir_path ))
    return 1
elif os.path.isdir( dst_dir_path ) == False:
    logger.error( "%s is not a folder. Please check path. " % ( dst_dir_path ))
    return 1

file_list = None
#transport = paramiko.Transport(( hostname, port)) 
paramiko.util.log_to_file('paramiko.log')
ssh = paramiko.SSHClient() 
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
#transport
try:
    ssh.connect( hostname, username=username, password=password, timeout=5.0) 
    #transport.connect(username=username, password=password ) 
except Exception, err:
    logger.error( "Failed to connect to the remote server. Reason: %s" % ( str(err) ) )
    return 1

try:
    #sftp = paramiko.SFTPClient.from_transport(transport)
    sftp = ssh.open_sftp() 
except Exception, err:
    logger.error( "Failed to start SFTP session from connection to %s. Check that SFTP service is running and available. Reason: %s" % ( hostname, str(err) ))
    return 1

try:    
    sftp.chdir(src_dir_path)
    #file_list = sftp.listdir(path="%s" % ( src_dir_path ) )
    file_list = sftp.listdir()

except Exception, err:
    logger.error( "Failed to list files in folder %s. Please check path and permissions. Reason: %s" % ( src_dir_path, str(err) ))
    return 1
match_text = re.compile( file_mask )
download_count = 0
for file in file_list:         
    # Here is an item name... but is it a file or directory?         
    #logger.info( "Downloading file %s." % ( file ) )
    if not re.match( file_mask, file ):
        continue
    else:
        logger.info( "File \"%s\" name matched file mask \"%s\". matches %s.Processing file..." % ( file, file_mask, (match_text.match( file_mask ) ) ) )
    src_file_path = "./%s" % ( file )
    dst_file_path = "/".join( [ dst_dir_path, file]   )
    retry_count = 0
    while True:
        try:
            logger.info( "Downloading file %s to %s."  % ( file, dst_file_path ) )
            #sftp.get( file, dst_file_path, callback=printTotals ) #sftp.get( remote file, local file )
            sftp.get( file, dst_file_path) #sftp.get( remote file, local file )
            logger.info( "Successfully downloaded file %s to %s."  % ( file, dst_file_path ) )
            download_count += 1
            break
        except Exception, err:
            if retry_count == retry_threshold:
                logger.error( "Failed to download %s to %s. Reason: %s." % ( file, dst_file_path, str(err) ) )
                sftp.close() 
                #transport.close()
                return 1
            else:
                logger.error( "Failed to download %s to %s. Reason: %s." % ( file, dst_file_path, str(err) ) )
                retry_count +=1

sftp.close() 
transport.close() 
logger.info( "%d files downloaded." % ( download_count ) )
return 0

以下の関数を実行すると、1〜1.6 GBのファイルのうち38〜41 MB(さまざま)しかダウンロードされていなくても、ソースファイルを約3分間ダウンロードしてから、セッションを閉じます。

Paramikoログファイルから、SFTPセッションが閉じている間、SSh接続は開いたままであるように見えます。

hmac-sha1'、' hmac-sha1-96'、' hmac-md5'、' hmac-md5-96'、' umac-64@openssh.com'] server mac:[' hmac-sha1'、' hmac- sha1-96'、' hmac-md5'、' hmac-md5-96'、' umac-64@openssh.com'] client compress:[' zlib@openssh.com'、' zlib'、' none'] server compress:['zlib@openssh.com'、'zlib'、'none'] client lang:[''] server lang:[''] kex follow?False DEB [20120913-10:05:07.421] thr = 1 paramiko.transport:同意された暗号:local = aes128-ctr、remote = aes128-ctr DEB [20120913-10:05:07.421] thr = 1 paramiko.transport:kexdiffie-hellman-group1-sha1を使用; サーバーキータイプssh-dss; 暗号:ローカルaes128-ctr、リモートaes128-ctr; mac:ローカルhmac-sha1、リモートhmac-sha1; 圧縮:ローカルなし、リモートなしDEB[20120913-10:05:07。625] thr = 1 paramiko.transport:新しいキーに切り替えます... INF [20120913-10:05:10.374] thr = 2 paramiko.transport.sftp:[chan1]sftpセッションが閉じられました。DEB [20120913-10:05:10.388] thr = 2 paramiko.transport:[chan 1] EOF送信(1)

この時点以降、スクリプトはこの例外を除いて終了します(sftp.get()try / exceptionブロックから)

リクエストを完了するのに十分なリソースがありません

システム自体にはギガバイトの空きディスク容量があるため、問題はありません。

parakmikoが失敗するのと同じ転送は、FileZillaと、SFTP転送を行うために何年も前に書いたJavaアプリで正常に機能します。ですから、paramikoの問題だと思います。

これは、WindowsXPおよびWindowsServer2003で実行されています。

より頻繁にキーを更新するようにParamko1.17にパッチを適用しようとしましたが、転送によって例外がスローされます。Python 2.7.3Paramiko1.7とパッチWindows2003Sevfer

アイデア?

追加情報:Windows XPSP3およびWindows2003サーバーでは失敗し、まったく同じ動作とエラーメッセージが表示されます。sys.version情報ウィンドウXPワークステーション: '2.7.3(デフォルト、2012年4月10日、23:31:26)[MSC v.1500 32ビット(Intel)]' Windows 2003サーバー: '2.7.3(デフォルト、4月10日) 2012、23:31:26)[MSC v.1500 32ビット(Intel)]'キーの更新間の時間を短縮するためにpacket.pyファイルにパッチを適用しました。sftp.get()の動作には影響しませんでした。

4

7 に答える 7

16

SFTP プロトコルには、ファイル データをストリーミングする方法がありません。代わりに、開いているファイルの特定のオフセットからデータのブロックを要求する方法があります。ファイルをダウンロードする単純な方法は、最初のブロックを要求し、それをディスクに書き込み、次に 2 番目のブロックを要求するというものです。これは信頼できますが、非常に遅いです。

代わりに、Paramiko には、使用するパフォーマンス トリックがあります。呼び出すと、ファイル内のすべて.get()のブロックに対してすぐに要求が送信され、書き込まれるはずのオフセットが記憶されます。次に、各応答が到着すると、それがディスク上の正しいオフセットに書き込まれることを確認します。詳細については、Paramiko ドキュメントのおよびメソッドを参照してください。1 GB のファイルをダウンロードするときに保存される帳簿情報が原因で、リソースが不足し、「リソースが不足しています」というメッセージが表示されている可能性があると思われます。SFTPFile.prefetch()SFTPFile.readv()

を使用するのではなく、.get()単にインスタンス.open()を取得するために呼び出す場合は、そのオブジェクトを呼び出すか、単に Python 標準ライブラリ関数に渡してコンテンツをダウンロードします。これにより、Paramiko プリフェッチ キャッシュが回避され、それほど高速でなくてもファイルをダウンロードできるようになります。SFTPFile.read()shutil.copyfileobj()

すなわち:

 def lazy_loading_ftp_file(sftp_host_conn, filename):
    """
        Lazy loading ftp file when exception simple sftp.get call
        :param sftp_host_conn: sftp host
        :param filename: filename to be downloaded
        :return: None, file will be downloaded current directory
    """
    import shutil
    try:
        with sftp_host_conn() as host:
            sftp_file_instance = host.open(filename, 'r')
            with open(filename, 'wb') as out_file:
                shutil.copyfileobj(sftp_file_instance, out_file)
            return {"status": "sucess", "msg": "sucessfully downloaded file: {}".format(filename)}
    except Exception as ex:
        return {"status": "failed", "msg": "Exception in Lazy reading too: {}".format(ex)}
于 2013-01-08T07:36:25.587 に答える
13

pysftp を使用して SFTP 経由で大きなファイル (>1 GB) をダウンロードする際に問題が発生しました。基盤となるライブラリは Paramiko です。問題についてグーグルで検索すると、ここに私が導かれ、素晴らしい解決策があります。それにもかかわらず、多くの投稿は比較的古く、これらの問題の大部分は時間の経過とともに解決されていると思います. そして、それは私の問題を解決しませんでした。

つまり、sftp_file.py でプリフェッチ中にチャンクをロードしているときに、Paramiko でメモリ エラーが発生します。リストが制限を超えて大きくなり、メモリ エラーが何らかの形で実行をブロックしていませんでした。おそらく、スタック上の何らかの方法で静かに消費されました。ダウンロードは、このエラーが発生した場合にのみ失敗し、別のスレッドで実行されます。

とにかく、リストのサイズを制御する方法は MAX_REQUEST_SIZE を設定することです:

paramiko.sftp_file.SFTPFile.MAX_REQUEST_SIZE = pow(2, 22) # 4MB per chunk

ただし、16MB を超えると、新しい問題が発生します: paramiko.sftp.SFTPError: Garbage packet received. _read_packet メソッドの sftp.py にチェックがあることが判明しました。

# most sftp servers won't accept packets larger than about 32k, so
# anything with the high byte set (> 16MB) is just garbage.
if byte_ord(x[0]):
    raise SFTPError("Garbage packet received")

したがって、チャンクが 16MB を超える場合、このエラーが発生します。私は Paramiko ライブラリ自体をいじる気は​​なかったので、チャンク サイズを 4MB の「許容できる最大値」に保つ必要がありました。

この方法で、サイズが 30GB を超えるファイルをダウンロードできました。これが人々に役立つことを願っています。

于 2019-10-11T10:47:23.630 に答える
5

スクリューテープの答えに加えて、おそらくブロックサイズを制限する必要があることにも言及する価値があります.read([block size in bytes])

大きなファイルを読み取るための遅延メソッドを参照してください

file.read()2.4 でブロック サイズ サイズがないだけで実際の問題がありましたが、2.7 が正しいブロック サイズを決定する可能性があります。

于 2013-03-07T12:00:41.727 に答える
-1

私も同様の問題に遭遇していました。

Traceback (most recent call last):
File "---", line 948, in <module>
main()
File "---", line 937, in main
args.sshProxyKeyfile)
File "---", line 904, in Bootstrap
CopyFiles(client, builds, k8sVer)
File "---", line 393, in CopyWcpFilesToVC
ScpWithClient(client, __file__, __file__)
File "---", line 621, in ScpWithClient
with client.open_sftp() as sftp:
File "---_env.Linux/lib64/python3.5/site-packages/paramiko/client.py", line 556, in open_sftp
return self._transport.open_sftp_client()
File "---_env.Linux/lib64/python3.5/site-packages/paramiko/transport.py", line 1097, in open_sftp_client
return SFTPClient.from_transport(self)
File "---_env.Linux/lib64/python3.5/site-packages/paramiko/sftp_client.py", line 170, in from_transport
return cls(chan)
File "---_env.Linux/lib64/python3.5/site-packages/paramiko/sftp_client.py", line 130, in __init__
server_version = self._send_version()
File "---_env.Linux/lib64/python3.5/site-packages/paramiko/sftp.py", line 134, in _send_version
t, data = self._read_packet()
File "---_env.Linux/lib64/python3.5/site-packages/paramiko/sftp.py", line 205, in _read_packet
raise SFTPError("Garbage packet received")
paramiko.sftp.SFTPError: Garbage packet received

その理由は、bash がユーザー ログインに関連付けられたデフォルトのシェルではなかったためです。ユーザーのデフォルト シェルを恒久的に変更するとchsh -s /bin/bash <user>、問題が修正されました。

于 2020-12-10T06:12:37.960 に答える