前回に続き、pysmbを使ってファイルの受信をしてみたいと思います。
インデックス
pysmbの使い方(接続・切断)
pysmbの使い方(ファイル受信)
pysmbの使い方(ファイル送信)
pysmbの使い方(ロギング)
pysmbの使い方(匿名接続)
実行環境
クライアント
- Windows 10
- Python 3.6.3
- pysmb 1.1.22
サーバー
- Windows 7
- User : IEUser
- Pass : Passw0rd!
- HostName : IEWIN7
- Domain : WORKGROUP
- IPAddress : 172.28.0.198
サーバーの共有フォルダShare
にsource.txt
を配置している。
ファイル受信
ファイル受信は以下のように行います。(インスタンスの作成や接続・切断処理は割愛)
with open('dst.txt', 'wb') as file:
conn.retrieveFile('Share', 'source.txt', file)
source.txt
が受信するサーバー側のファイル、dst.txt
はクライアント側に作成されるファイルです。
SMBConnection#retrieveFile
メソッドを使用します。定義は以下の通り。
def retrieveFile(self, service_name, path, file_obj, timeout = 30):
- service_name
サーバー側の共有フォルダ名 - path
目的のファイルのパス - file_obj
書き込むファイルのファイルオブジェクトを渡してあげる - timeout
タイムアウト値です。デフォルト30秒
フォルダがある場合
受信先がフォルダ配下の場合、当然フォルダは存在していなければなりません。
with open('tmp/dst.txt', 'wb') as file:
conn.retrieveFile('Share', 'source.txt', file)
この場合でtmp
フォルダがない場合、FileNotFoundError
になります。
受信元がフォルダ配下にある場合、下記のように指定すれば良いです。
with open('dst.txt', 'wb') as file:
conn.retrieveFile('Share', 'tmp/source.txt', file)
ちなみに受信元のファイルが存在しない場合はsmb_structs.OperationFailure
になります。
戻り値
必要になることはまず無いと思いますが…retrieveFile()
の戻り値として**ファイル属性(int)と受信バイト数(int)**のタプルを受け取ることができます。
ファイル属性とはsmb_constants.py
に定義されているSMB_FILE_ATTRIBUTE_xxxのビット論理和です。
中身しかいらない場合
ファイル自身を受信する必要は無くファイルの中身だけ読めればいい場合、io.BytesIO()
を使うことで内容だけ受信できます。
import io
with io.BytesIO() as file:
conn.retrieveFile('Share', 'source.txt', file)
file.seek(0)
print(file.read().decode())
decode()
のエンコーディングはデフォルトでutf-8
なので、例えばshift-jis
のファイルであれば以下のように指定することで読み込めます。
with io.BytesIO() as file:
conn.retrieveFile('Share', 'source.txt', file)
file.seek(0)
print(file.read().decode('shift-jis'))
ファイル一覧の取得
サーバーの共有フォルダにどのようなファイルがあるか知りたい場合、SMBConnection#listPath
メソッドを利用してファイルのリストを取得することができます。
files = conn.listPath('Share', '/')
上記の例では共有フォルダ直下のファイル(フォルダ)のみが取得できます。共有フォルダ内にあるフォルダ配下のリストを取得したい場合は、path
に目的のフォルダパスを設定します。
# /tmp フォルダのリストを取得
files = conn.listPath('Share', '/tmp')
# /tmp/hoge フォルダのリストを取得
files = conn.listPath('Share', '/tmp/hoge')
この時path
のルート('/'
)はあっても無くてもいいです。
共有フォルダ直下のリストを取得する場合は'/'
でも''
でもいいし、tmpフォルダのリストを取得する場合は'/tmp'
でも'tmp'
でもいいです。
少し詳しく見てみます、SMBConnection#listPath
の定義は以下の形です。
def listPath(self, service_name, path,
search = SMB_FILE_ATTRIBUTE_READONLY | SMB_FILE_ATTRIBUTE_HIDDEN | SMB_FILE_ATTRIBUTE_SYSTEM | SMB_FILE_ATTRIBUTE_DIRECTORY | SMB_FILE_ATTRIBUTE_ARCHIVE,
pattern = '*', timeout = 30):
- service_name
サーバー側の共有フォルダ名 - path
ファイルリストを取得するフォルダパス - search
検索対象の絞り込みをSMB_FILE_ATTRIBUTE_xxxのビット論理和で指定する。
ただしSMB1
でのみ有効、SMB2
を使用する場合この値は使われない。 - pattern
検索結果のフィルタ、例えばテキストのみ抽出する場合は*.txt
のように指定する。 - timeout
タイムアウト値です。デフォルト30秒
SharedFile
SMBConnection#listPath
の結果はSharedFile
インスタンスの配列で返って来ます。
その為純粋なファイル名のみが必要な場合は一工夫必要です。
files = [f.filename for f in conn.listPath('Share', '/')]
ここで返ってくるSharedFile
クラスは共有フォルダから取得できるファイル/フォルダの情報を持ちます。具体的には以下のようなプロパティです。
- filename
ファイル名(もしくはフォルダ名) - short_name
8.3形式の短いファイル名 - file_attributes
SMB_EXT_FILE_ATTR
の論理和、詳しくは[MS-CIFS]: 2.2.1.2.3を参照 - alloc_size
ファイルを格納する為割り当てられたサイズ、いわゆるディスク上のサイズ(Byte) - file_size
ファイルのサイズ(Byte) - create_time
1970-01-01 00:00:00からの経過秒数でファイル作成日時を表す。 - last_access_time
1970-01-01 00:00:00からの経過秒数で最終ファイルアクセス日時を表す。 - last_write_time
1970-01-01 00:00:00からの経過秒数で最終ファイル更新日時を表す。 - last_attr_change_time
1970-01-01 00:00:00からの経過秒数で最終ファイル属性変更日時を表す。 - isDirectory
フォルダかどうか - isReadOnly
読み取り専用かどうか
あくまで情報のみを持つクラスでこのインスタンスから直接受信処理などを行うことはできません。filename
を用いて別途retrieveFile()
で受信しなければならないです。
共有フォルダのファイルを全部列挙
listPath()
である程度深さのある共有フォルダからリストを取得したい場合、フォルダを指定しなければならないので若干取り回しが悪い(と思います)。
なので以下のように共有フォルダ以下(もしくは指定のフォルダ以下)のファイルを全て列挙するようにして使うと扱いやすいです。ただファイルがべらぼうに多いと非常に時間が掛かるので見に行くフォルダがどの程度の規模かは知っておく必要があります。
def list_all(service_name, path=''):
entries = conn.listPath(service_name, path)
for item in (e for e in entries if not e.filename in ['.', '..']):
if item.isDirectory:
yield from list_all(service_name, path=f'{path}/{item.filename}')
else:
yield f'{path}/{item.filename}'
print([f for f in list_all('Share')])
共有フォルダ一覧の取得
SMBConnection#listShares
メソッドではサーバーの共有フォルダをリストで取得することができます。
SharedDevice
のリストを戻り値として受け取ることができます。フォルダ名一覧を取得する場合は以下のように使用します。
print([dev.name for dev in conn.listShares()])
SMBConnection#listShares
の引数はtimeout
のみです。デフォルト30秒でございます。
SharedDevice
SharedDevice
はSharedFile
のように、共有フォルダの情報を持ちます。以下のプロパティを持ちます。
- name
共有フォルダ名です。 - comments
共有フォルダのコメントです。 - isSpecial
IPC$
やADMIN$
、C$
、D$
など予約された共有名の場合True
を返します。 - isTemporary
データが永続化されない一時的な共有の場合True
を返します。 - type
共有フォルダのタイプです。詳しくは[MS-SRVS]: 2.2.2.4を参照
読まなくてもいい話
読まなくてもいい話というか、どう使えばいいかわからないメソッド達
retrieveFileFromOffset
retrieveFile()
に対して、バイト単位でファイルの一部を受信できるretrieveFileFromOffset()
というメソッドもあります…が、敢えてこのメソッドを使うシーンはほとんど無いと思います。
ただretrieveFile()
では最初から最後までという条件(offset=0, max_length=-1)で、内部的にretrieveFileFromOffset()
を利用してファイル受信しているようです。
listSnapshots
Windows7以降(厳密にはVista Business以降?)利用可能なフォルダのスナップショット機能が有効な場合、その一覧を取得することができます。
が、戻り値はdatetime
のリストです…使い道がわからない。