IronPython で WebDAV を使ってファイルをアップロード
以下のように .NET の WebClient や WebRequest クラスを使えば、WebDAV の操作が行える。
コレクションの作成
WebClient の UploadString 等を使って URI、MKCOL を指定するだけなので非常に簡単
http://localhost/webdav/ に test1 フォルダを作成する例
from System import * from System.Net import * wc = WebClient() wc.BaseAddress = "http://localhost/webdav/" wc.UploadString("test1", "MKCOL", "")
リソースの作成
WebClient の UploadFile を使って URI、PUT、ファイル名を指定するだけなので非常に簡単
../../data1.txt ファイルを http://localhost/webdav/test1/data1.txt というリソースとして作成する例
from System import * from System.Net import * wc = WebClient() wc.BaseAddress = "http://localhost/webdav/" wc.UploadFile("test1/data1.txt", "PUT", "../../data1.txt")
大きなデータをアップロードする際の注意点
実は、HttpWebRequest(WebClient は内部で使用) を使ってサイズの大きなファイルをアップロードしたり WebDAV に POST・PUT する場合に out-of-memory やタイムアウトが発生して失敗することがある。
これは以下の仕様に起因するもので、
- HttpRequest クラスはデフォルトでデータをバッファリングする
回避するためには、以下のようにバッファリングを停止する必要がある。
- AlowWriteStreamBuffering プロパティに False を設定
ただし、上記の回避策を認証機能と共に利用すると ProtocolError - 「この要求には、データのバッファが必要です」というエラーが発生する。
そのため、これを回避するには以下のような回避策を施す。
- PreAuthenticate に False を設定し最小限のリクエスト送信(#1)で認証を終える
- PreAuthenticate と AllowWriteStreamBuffering に False を設定し、巨大なデータを POST や PUT する
(#1)は基本的に認証が終了するアクセスであれば何でも良く、通常は HEAD メソッドを使用すればよいが、その場合は指定する URL が予め存在している必要がある。
IronPython でサイズの大きいファイルを WebDAV を使って登録するサンプル
from System import * from System.IO import * from System.Net import * url = "http://localhost/test/" postUrl = url + "default_dest.wmv" file = "default.wmv" req = WebRequest.Create(postUrl) ccache = CredentialCache() ccache.Add(Uri(url), "Digest", NetworkCredential("test", "testpass")) req.Credentials = ccache req.Method = "PUT" req.PreAuthenticate = True #存在しない URL に HEAD メソッドは使えないため、 #PUT で空のファイルを作成して認証を終える #(ファイルは何でも良い) req.PreAuthenticate = True req.Method = "PUT" req.GetRequestStream().Close() req.GetResponse().Close() #一度 書き込みのストリームを開いた HttpWebRequest オブジェクトでは # AllowWriteStreamBuffering の設定が変更できないため再インスタンス化 req = WebRequest.Create(postUrl) req.Credentials = ccache #AllowWriteStreamBuffering を false にして有効に通信するためには #PreAuthenticate で事前認証済みでなければならない req.AllowWriteStreamBuffering = False req.PreAuthenticate = True req.Method = "PUT" #ファイルアップロード try: fi = FileInfo(file) req.ContentLength = fi.Length fs = fi.OpenRead() st = req.GetRequestStream() buf = Array.CreateInstance(Byte, 1024 * 100); len = 0 try: while True: len = fs.Read(buf, 0, buf.Length) if len > 0: st.Write(buf, 0, len) st.Flush() else: break finally: fs.Close() fs.Dispose() st.Close() req.GetResponse().Close() except WebException, ex: print "%s, %s" % (ex.Status, ex.Message)