FTP(SFTP)による高頻度なファイル転送を実装する機会があったので、
備忘録も兼ねてまとめました。
PythonでのFTP
PythonにはFTP周りのインタフェースを提供するライブラリftplib
があり、
これを用いることでFTPサーバとの通信プログラムを簡単に実装することができます。
ローカルファイルを転送する場合はこんな感じ:
from ftplib import FTP
def main():
# 接続
ftp = FTP("example.com", "username", passwd = "password")
# FTP.storlinesにファイルオブジェクトを渡す
with open("test.txt", "rb") as f:
ftp.storlines("STOR ./test.txt", f)
# 切断
ftp.quit()
if __name__ == "__main__":
main()
しかし、この方法ではプログラム実行中に出力されたメモリ上のデータを転送することができません。
いちいちファイルに出力するのは冗長ですし、何よりストレージへの負荷がすごそうです。
オンメモリでファイルオブジェクトを作る
そこで、StringIO
およびBytesIO
を用いてストリームを生成し、ftplibに渡す方法を考えます。
引用: Pythonドキュメント 「io --- ストリームを扱うコアツール」
インメモリー ストリーム
str や bytes-like オブジェクト を、読み書き可能なファイルのように扱うことができます。 StringIO は文字列に対して、テキストモードで開かれたファイルのように使うことができます。 BytesIO はバイナリーモードで開いたファイルのように扱うことができます。この2つのクラスは、読み書き可能で、ランダムアクセス可能です。
storlines
はバイナリモードで開いたファイルのオブジェクトを渡す必要があるので、今回はio.BytesIO
を使用します。
import io
def main():
# io.BytesIOオブジェクトを生成
bytesIO = io.BytesIO(bytes("Hello, World!!", encoding="utf-8"))
print(type(bytesIO))
# ファイルオブジェクトのように操作する
with bytesIO as b:
print(b.readlines())
if __name__ == "__main__":
main()
# 実行結果
# <class '_io.BytesIO'>
# [b'Hello, World!!']
BytesIO
オブジェクトが生成され、with文で読むとbytes
が返ってきます。
ftplibに渡して転送する
では、このBytesIO
オブジェクトをstorlines
に渡してみます。
import io
from ftplib import FTP
def main():
# io.BytesIOでファイルっぽいものを作り、
bytesIO = io.BytesIO(bytes("Hello, World!!", encoding="utf-8"))
print(type(bytesIO))
# FTP.storlinesに渡して転送する
ftp = FTP("example.com", "username", passwd = "password")
ftp.storlines("STOR ./test.txt", bytesIO)
ftp.quit()
if __name__ == "__main__":
main()
実行後、サーバをのぞくと…
[username@example.com ~]$ cat test.txt
Hello, World!!
記述した文字列が書き込まれたファイルtest.txt
が作成されていることがわかります。
まとめ
ここまで読んでいただきありがとうございました。
まとめで言うのもアレですが、こんな面倒なことをするよりHTTPSを使うべきだと思います。
FTPはFile Transfer Protocolなので。
ではまた!