C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[2]: WebDAVサーバーファイルアップロードのエラー


(過去ログ 126 を表示中)

[トピック内 10 記事 (1 - 10 表示)]  << 0 >>

■74767 / inTopicNo.1)  WebDAVサーバーファイルアップロードのエラー
  
□投稿者/ DSC渡邊 (4回)-(2015/01/28(Wed) 15:24:15)
DSC渡邊 さんの Web サイト

分類:[VB.NET/VB2005 以降] 

以前、この掲示板でvb.netでWebDAVサーバーに大容量ファイルをアップロードするとエラーになってしまうと質問し、解決したのですが、
サーバーを移動したところ、アップロード処理が正常に動作しないようになってしまいました。


実行すると1〜2分停滞した後、
「基礎になる接続が閉じられました: 受信時に予期しないエラーが発生しました」
という例外エラーが発生してしまいます。

サーバー管理者に確認したところ、サーバーの設定は移動前から変えていないとのことでした。


ソースコードは以下のとおりです
−−−−−−−−−−−−−−−−

            Dim fileToUpload As String = ""
            Dim fileLength As Long = 0
            Dim wIdx As Long = 0
            Dim url As String = ""

            url = サーバーのURL
            fileToUpload = アップロードするファイルのフルパス
            fileLength = My.Computer.FileSystem.GetFileInfo(fileToUpload).Length
            Dim u As New Uri(url)
            Dim host As String = u.Host
            url = url.TrimEnd("/"c) & "/" & サーバーアップロード先フォルダ & IO.Path.GetFileName(fileToUpload)
            Dim request As HttpWebRequest = DirectCast(System.Net.HttpWebRequest.Create(url), HttpWebRequest)
            request.Credentials =  New NetworkCredential(ユーザー名, パスワード)
            request.Method = WebRequestMethods.Http.Put
            request.ContentLength = fileLength
            request.SendChunked = True
            request.Headers.Add("Translate: f")  
            request.AllowWriteStreamBuffering = False
            request.Timeout = 1800000               ''タイムアウト1800秒
            Dim s As IO.Stream = request.GetRequestStream()
            Dim fs As New IO.FileStream(fileToUpload, IO.FileMode.Open, _
                        IO.FileAccess.Read)
            Dim byteTransferRate As Integer = 102400
            Dim bytes(byteTransferRate - 1) As Byte
            Dim bytesRead As Integer = 0
            Dim totalBytesRead As Long = 0
            Do
                bytesRead = fs.Read(bytes, 0, bytes.Length)
                If bytesRead > 0 Then
                    totalBytesRead += bytesRead
                    s.Write(bytes, 0, bytesRead)
                End If
            Loop While bytesRead > 0
            s.Close()
            s.Dispose()
            s = Nothing
            fs.Close()
            fs.Dispose()
            fs = Nothing
            Dim response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
            Dim code As HttpStatusCode = response.StatusCode
            response.Close()
            response = Nothing

−−−−−−−−−−−−−−−−

自分でも、色々と調べたり、設定を変えたりしてみたところ、
「request.SendChunked = True」を「False」に、「request.AllowWriteStreamBuffering = False」を「True」に変更して
実行するとアップロードに成功しました。ただし、大容量(800MBほど)のファイルをアップロードしようとすると処理が停滞し、
タイムアウトエラーとなってしまいました。
※「request.SendChunked = False」の設定をして、「request.AllowWriteStreamBuffering = False」のままだと「この要求には、データのバッファーが必要です。」というエラーになってしまう為、「request.AllowWriteStreamBuffering = False」を「True」にしました。

比較的小さなファイル(50MBや130MB)は10〜数十秒程度で正常にアップロードできました。
ちなみに「CarotDAV」のようなツールソフトだと大容量ファイルも正常にアップロードできました。


何が原因で動作しなくなったのか、分からなくて困っています。教えてほしいのは以下の点です。
@「基礎になる接続が閉じられました: 受信時に予期しないエラーが発生しました」の発生原因の調べ方
A上記の修正内容で大容量のファイルアップロードを成功させる方法があれば教えてほしいです。
B上記以外の修正方法で、アップロードを動作させる方法があれば教えてほしいです。

よろしくお願いします。




引用返信 編集キー/
■74770 / inTopicNo.2)  Re[1]: WebDAVサーバーファイルアップロードのエラー
□投稿者/ PANG2 (56回)-(2015/01/28(Wed) 16:23:42)
> @「基礎になる接続が閉じられました: 受信時に予期しないエラーが発生しました」の発生原因の調べ方

クライアント側

HTTPやTCPをモニタするツール

サーバ側

アクセスログ
引用返信 編集キー/
■74771 / inTopicNo.3)  Re[2]: WebDAVサーバーファイルアップロードのエラー
□投稿者/ れい (1回)-(2015/01/28(Wed) 17:06:18)
No74767 (DSC渡邊 さん) に返信
> 何が原因で動作しなくなったのか、分からなくて困っています。教えてほしいのは以下の点です。
> @「基礎になる接続が閉じられました: 受信時に予期しないエラーが発生しました」の発生原因の調べ方

ネットワークのトレースを行うか、
WireSharkなどパケットキャプチャソフトを使うのがよいかと思います。
サーバー側のログや、間にルーターやプロキシがあるならそのログも参考になります。

> A上記の修正内容で大容量のファイルアップロードを成功させる方法があれば教えてほしいです。
> B上記以外の修正方法で、アップロードを動作させる方法があれば教えてほしいです。

サーバーを変えて動かなくなったのであれば
うまく動くサーバーと動かないサーバーの「差」を検証すればよいかと。

サーバーの詳細、認証方法などが明示されていないので私の経験からの推測になりますが、
単に認証で蹴られているだけかと思います。
ですので下記の方法で試すとよいかと。

方法@ 空PUT

まず、
HttpWebRequest.ContentLength=0
を指定してPUTで目的のファイルと同名の空ファイルを作成。
その後すぐに
HttpWebRequest.ContentLength=正しいファイルサイズ
HttpWebRequest.AllowWriteStreamBuffering = false
HttpWebRequest.SendChunked=false
としてもう一度上書きでPUT。

方法A PreAuth

HttpWebRequest.PreAuthenticate = true
HttpWebRequest.ContentLength=正しいファイルサイズ
HttpWebRequest.AllowWriteStreamBuffering = false
HttpWebRequest.SendChunked=false
としてPUT


サーバーのアクセス権の設定や認証方式によっては上記ではダメな場合があります。
その場合はサーバーの設定などの詳細があればわかるかもしれません。

それと

> ※「request.SendChunked = False」の設定をして、「request.AllowWriteStreamBuffering = False」のままだと「この要求には、データのバッファーが必要です。」というエラーになってしまう為、「request.AllowWriteStreamBuffering = False」を「True」にしました。

ContentLengthを設定すればエラーは出ないはずです。

引用返信 編集キー/
■74849 / inTopicNo.4)  Re[2]: WebDAVサーバーファイルアップロードのエラー
□投稿者/ DSC渡邊 (5回)-(2015/02/02(Mon) 08:09:48)
DSC渡邊 さんの Web サイト
No74770 (PANG2 さん) に返信
>>@「基礎になる接続が閉じられました: 受信時に予期しないエラーが発生しました」の発生原因の調べ方
>
> クライアント側
>
> HTTPやTCPをモニタするツール
>
> サーバ側
>
> アクセスログ

返信が遅れてすいません。(別の業務が入ってしまって…)

教えてくれてありがとうございます。
なんとか調べてみます。
引用返信 編集キー/
■74850 / inTopicNo.5)  Re[3]: WebDAVサーバーファイルアップロードのエラー
□投稿者/ DSC渡邊 (6回)-(2015/02/02(Mon) 08:33:50)
DSC渡邊 さんの Web サイト
No74771 (れい さん) に返信
> ■No74767 (DSC渡邊 さん) に返信
>>何が原因で動作しなくなったのか、分からなくて困っています。教えてほしいのは以下の点です。
>>@「基礎になる接続が閉じられました: 受信時に予期しないエラーが発生しました」の発生原因の調べ方
>
> ネットワークのトレースを行うか、
> WireSharkなどパケットキャプチャソフトを使うのがよいかと思います。
> サーバー側のログや、間にルーターやプロキシがあるならそのログも参考になります。
>
>>A上記の修正内容で大容量のファイルアップロードを成功させる方法があれば教えてほしいです。
>>B上記以外の修正方法で、アップロードを動作させる方法があれば教えてほしいです。
>
> サーバーを変えて動かなくなったのであれば
> うまく動くサーバーと動かないサーバーの「差」を検証すればよいかと。
>
> サーバーの詳細、認証方法などが明示されていないので私の経験からの推測になりますが、
> 単に認証で蹴られているだけかと思います。
> ですので下記の方法で試すとよいかと。
>
> 方法@ 空PUT
>
> まず、
> HttpWebRequest.ContentLength=0
> を指定してPUTで目的のファイルと同名の空ファイルを作成。
> その後すぐに
> HttpWebRequest.ContentLength=正しいファイルサイズ
> HttpWebRequest.AllowWriteStreamBuffering = false
> HttpWebRequest.SendChunked=false
> としてもう一度上書きでPUT。
>
> 方法A PreAuth
>
> HttpWebRequest.PreAuthenticate = true
> HttpWebRequest.ContentLength=正しいファイルサイズ
> HttpWebRequest.AllowWriteStreamBuffering = false
> HttpWebRequest.SendChunked=false
> としてPUT
>
>
> サーバーのアクセス権の設定や認証方式によっては上記ではダメな場合があります。
> その場合はサーバーの設定などの詳細があればわかるかもしれません。
>
> それと
>
>>※「request.SendChunked = False」の設定をして、「request.AllowWriteStreamBuffering = False」のままだと「この要求には、データのバッファーが必要です。」というエラーになってしまう為、「request.AllowWriteStreamBuffering = False」を「True」にしました。
>
> ContentLengthを設定すればエラーは出ないはずです。
>


返信が遅れてすいません。

丁寧な内容で教えてくれてありがとうございます。
試してみます。

引用返信 編集キー/
■74871 / inTopicNo.6)  Re[3]: WebDAVサーバーファイルアップロードのエラー
□投稿者/ DSC渡邊 (7回)-(2015/02/04(Wed) 18:07:06)
DSC渡邊 さんの Web サイト
No74771 (れい さん) に返信

 返信が遅くなって申し訳ありません。
 
 教えていただいた空ファイルをアップロードした後、目的の大容量ファイルを続けてアップロードするという方法を取ったところ、無事にアップロードに成功しました。

成功したソースコードは以下のとおりです
(もっと上手なコードの書き方があるのかもしれませんが・・・)
−−−−−−−−−−−−−−−−

Dim fileToUpload As String = ""
Dim fileLength As Long = 0
Dim wIdx As Long = 0
Dim url As String = ""

 url = サーバーのURL
''空ファイルPUT
fileToUpload = 空ファイル(0バイト)のフルパス
fileLength = My.Computer.FileSystem.GetFileInfo(fileToUpload).Length
url = url.TrimEnd("/"c) & "/" & サーバーのアップロード先フォルダ & IO.Path.GetFileName(fileToUpload)
Dim request As HttpWebRequest = DirectCast(System.Net.HttpWebRequest.Create(url), HttpWebRequest)
request.Credentials = New NetworkCredential(ユーザー名, パスワード)
request.Method = WebRequestMethods.Http.Put
request.ContentLength = fileLength
request.SendChunked = False
request.AllowWriteStreamBuffering = False
request.PreAuthenticate = True
request.Timeout = 480000
request.Headers.Add("Translate: f")

Dim s As IO.Stream = request.GetRequestStream()
Dim fs1 As New IO.FileStream(fileToUpload, IO.FileMode.Open, _
IO.FileAccess.Read)
Dim byteTransferRate As Integer = 102400
Dim bytes(byteTransferRate - 1) As Byte
Dim bytesRead As Integer = 0
Dim totalBytesRead As Long = 0

bytesRead = fs1.Read(bytes, 0, bytes.Length)
s.Write(bytes, 0, bytesRead)
s.Close()
s.Dispose()
s = Nothing
fs1.Close()
fs1.Dispose()
fs1 = Nothing
request.GetRequestStream().Close()
request.GetResponse().Close()

url = サーバーのURL
fileToUpload = アップロードしたい大容量ファイルのフルパス
fileLength = My.Computer.FileSystem.GetFileInfo(fileToUpload).Length
url = url.TrimEnd("/"c) & "/" & サーバーのアップロード先フォルダ & IO.Path.GetFileName(fileToUpload)
request = DirectCast(System.Net.HttpWebRequest.Create(url), HttpWebRequest)
request.Credentials = New NetworkCredential(ユーザー名, パスワード)
request.Method = WebRequestMethods.Http.Put
request.ContentLength = fileLength
request.SendChunked = False
request.AllowWriteStreamBuffering = False
request.PreAuthenticate = True
request.Timeout = 480000
request.Headers.Add("Translate: f")
s = request.GetRequestStream()
Dim fs2 As New IO.FileStream(fileToUpload, IO.FileMode.Open, _
IO.FileAccess.Read)
bytesRead = 0
totalBytesRead = 0
Do
bytesRead = fs2.Read(bytes, 0, bytes.Length)
If bytesRead > 0 Then
totalBytesRead += bytesRead
s.Write(bytes, 0, bytesRead)
End If
Loop While bytesRead > 0
s.Close()
s.Dispose()
s = Nothing
fs2.Close()
fs2.Dispose()
fs2 = Nothing
Dim response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
Dim code As HttpStatusCode = response.StatusCode
response.Close()
response = Nothing

−−−−−−−−−−−−−−−−

ちなみにアップロードの時間は800MBで2分前後と以前よりも早くなりました。(サーバーが近い場所に移動されたからかもしれませんが…)

結局、何が原因でアップロードエラーが発生したのか、はっきりしませんでした。
サーバー管理者に確認しましたが、ファイルのアクセス権や認証方法も変更していないとのことでした。

大きなデータをアップロードする際の注意点について書かれたURLを見つけて、そこには「AlowWriteStreamBuffering プロパティに Falseを設定して認証機能と共に利用すると 「この要求には、データのバッファが必要です」というエラーが発生する 」と書かれていましたが…
今回のソース修正では、そちらのURLの内容も一部参考にさせていただきました。
参考URL
http://fits.hatenablog.com/entry/20070911/1189510041

どうもお手数をおかけしてすいませんでした。

ありがとうございました。




解決済み
引用返信 編集キー/
■74873 / inTopicNo.7)  Re[4]: WebDAVサーバーファイルアップロードのエラー
□投稿者/ れい (4回)-(2015/02/04(Wed) 22:45:11)
No74871 (DSC渡邊 さん) に返信
> 大きなデータをアップロードする際の注意点について書かれたURLを見つけて、そこには「AlowWriteStreamBuffering プロパティに Falseを設定して認証機能と共に利用すると 「この要求には、データのバッファが必要です」というエラーが発生する 」と書かれていましたが…

それは認証方式、AllowAutoRedirect、ContentLength、SendChunked等の設定によります。
プロトコル上、「長さ」の送信が必要になる場合と「再送」する可能性がある場合にバッファリングが必要になります。

> 結局、何が原因でアップロードエラーが発生したのか、はっきりしませんでした。

認証方式を書いていないのでわかりませんが、
設定を一切いじっていないということであればNTLMかKerberos等の接続指向の認証プロトコルを使っていたのではないですか?
通常HTTPはリクエスト毎に認証しますが、これらはTCP接続単位で認証するため、他の接続の影響を受けます。

認証用の空PUTはRFCに軽く述べられていますが、"Allow: PUT"とヘッダーを送り、書き込みたいURLと同じURLを使うべきです。
また、リダイレクトが発生することも考慮し、2回目のPUTではURLを書き換えておかなければなりません。
サーバーによってはセッションを保持しなければいけない場合もあります。
その他いくつかオマジナイを入れ、下記のようなコードがよいでしょう。(動作未確認ですが。

        Dim fileToUpload As String 
        Dim fileLength As Long 
        Dim url As Uri
        Dim nc As New NetworkCredential(ユーザー名, パスワード)
        Dim cc As New CookieContainer 'for tracking session
        Dim request As HttpWebRequest
        '空ファイルPUT
        fileToUpload = アップロードしたい大容量ファイルのフルパス
        Using fs As New IO.FileStream(fileToUpload, IO.FileMode.Open, IO.FileAccess.Read)
            fileLength = fs.Length
            url = New Uri(サーバーのURL, "/" & サーバーのアップロード先フォルダ & IO.Path.GetFileName(fileToUpload))

            request = DirectCast(System.Net.HttpWebRequest.Create(url), HttpWebRequest)
            request.Method = WebRequestMethods.Http.Put
            request.UnsafeAuthenticatedConnectionSharing = True 'for Nego/NTLM/Kerberos
            request.KeepAlive = True
            request.Headers.Add(HttpRequestHeader.Allow, "PUT") ' for EmptyPUT
            request.AllowWriteStreamBuffering = True
            request.PreAuthenticate = True
            request.Headers.Add("Translate: f") ' for IIS
            request.Credentials = nc
            request.CookieContainer = cc
            Using res As HttpWebResponse = request.GetResponse()
                url = res.ResponseUri
            End Using

            request = DirectCast(System.Net.HttpWebRequest.Create(url), HttpWebRequest)
            request.Method = WebRequestMethods.Http.Put
            request.UnsafeAuthenticatedConnectionSharing = True
            request.KeepAlive = True
            request.ContentLength = fileLength
            request.SendChunked = True
            request.AllowWriteStreamBuffering = False 'no-buffering
            request.PreAuthenticate = True ' for sharing auth info
            request.AllowAutoRedirect = False
            request.Headers.Add("Translate: f")
            request.Credentials = nc
            request.CookieContainer = cc

            Using reqst As IO.Stream = request.GetRequestStream()
                Dim buf As Byte() = New Byte(64 * 1024 - 1) {}
                Dim r As Byte
                While True
                    r = fs.Read(buf, 0, buf.Length)
                    If r = 0 Then Exit While
                    reqst.Write(buf, 0, r)
                End While
            End Using
        End Using

        Dim code As HttpStatusCode
        Using response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
            code = response.StatusCode
            If code >= 300 AndAlso code < 400 Then Throw New Exception
        End Using

引用返信 編集キー/
■74877 / inTopicNo.8)  Re[5]: WebDAVサーバーファイルアップロードのエラー
□投稿者/ DSC渡邊 (8回)-(2015/02/05(Thu) 09:57:34)
DSC渡邊 さんの Web サイト
No74873 (れい さん) に返信

懇切丁寧な説明ありがとうございます。助かります。

教えて下さったコードは早速参考にさせていただきました。
ただ、「 request.UnsafeAuthenticatedConnectionSharing = True 」  にすると「転送接続にデータを書き込めません」というエラーが発生しました。
どうやら、サーバーの認証プロトコルはNTLMかKerberos等ではないようです。

最終的には以下のようなソースコードでアップロードに成功しました。
−−−−−−−−−−−−−−−−

            Dim fileToUpload As String
            Dim fileLength As Long
            Dim url As Uri
            Dim nc As New NetworkCredential(ユーザー名, パスワード)
            Dim cc As New CookieContainer 'for tracking session
            Dim request As HttpWebRequest

            '空ファイルPUT
            fileToUpload = アップロード元ファイルのフルパス
            Using fs As New IO.FileStream(fileToUpload, IO.FileMode.Open, IO.FileAccess.Read)
                fileLength = fs.Length
                url = New Uri(サーバーのURL & "/" & サーバーのアップロード先フォルダ & IO.Path.GetFileName(fileToUpload))
                request = DirectCast(System.Net.HttpWebRequest.Create(url), HttpWebRequest)
                request.Method = WebRequestMethods.Http.Put
                request.Headers.Add(HttpRequestHeader.Allow, "PUT") ' for EmptyPUT
                request.AllowWriteStreamBuffering = True
                request.PreAuthenticate = True
                request.Headers.Add("Translate: f") ' for IIS
                request.Credentials = nc
                request.CookieContainer = cc
                request.ContentLength = 0
                Using res As HttpWebResponse = request.GetResponse()
                    url = res.ResponseUri
                End Using

                request = DirectCast(System.Net.HttpWebRequest.Create(url), HttpWebRequest)
                request.Method = WebRequestMethods.Http.Put
                request.ContentLength = fileLength
                request.SendChunked = False
                request.AllowWriteStreamBuffering = False 'no-buffering
                request.PreAuthenticate = True ' for sharing auth info
                request.AllowAutoRedirect = False
                request.Headers.Add("Translate: f")
                request.Credentials = nc
                request.CookieContainer = cc
                request.Timeout = 600000               ''タイムアウト600秒
                Using reqst As IO.Stream = request.GetRequestStream()
                    Dim bytes(102400 - 1) As Byte
                    Dim bytesRead As Integer = 0
                    Dim totalBytesRead As Long = 0
                    Do
                        bytesRead = fs.Read(bytes, 0, bytes.Length)
                        If bytesRead > 0 Then
                            totalBytesRead += bytesRead
                            reqst.Write(bytes, 0, bytesRead)
                        End If
                    Loop While bytesRead > 0
                End Using
            End Using

            Dim code As HttpStatusCode
            Using response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
                code = response.StatusCode
                If code >= 300 AndAlso code < 400 Then Throw New Exception
            End Using

−−−−−−−−−−−−−−−−

処理効率も良くなったようで、昨日の800MBのファイルで2分程かかったアップロード時間が1分30秒を切るようになりました。

本当にありがとうございました。

解決済み
引用返信 編集キー/
■74883 / inTopicNo.9)  Re[6]: WebDAVサーバーファイルアップロードのエラー
□投稿者/ れい (5回)-(2015/02/05(Thu) 14:06:12)
No74877 (DSC渡邊 さん) に返信
> ただ、「 request.UnsafeAuthenticatedConnectionSharing = True 」 にすると「転送接続にデータを書き込めません」というエラーが発生しました。
> どうやら、サーバーの認証プロトコルはNTLMかKerberos等ではないようです。

なにかおかしいですね。
UnsafeAuthenticatedConnectionSharingが原因でそのエラーになったことはありません。
NTLMやKerberosでなければ効果がないだけで、書き込めないことは通常ありません。

動作してるとはいえ全般に不可解な動作がおおいので、
インターネット経由であったり、個人情報を扱っているなど、
セキュリティ上シビアな案件であればきちんと問題を分析しておいた方がいいかもしれません。

引用返信 編集キー/
■74920 / inTopicNo.10)  Re[7]: WebDAVサーバーファイルアップロードのエラー
□投稿者/ DSC渡邊 (9回)-(2015/02/09(Mon) 17:51:20)
DSC渡邊 さんの Web サイト
No74883 (れい さん) に返信
> ■No74877 (DSC渡邊 さん) に返信
>>ただ、「 request.UnsafeAuthenticatedConnectionSharing = True 」 にすると「転送接続にデータを書き込めません」というエラーが発生しました。
>>どうやら、サーバーの認証プロトコルはNTLMかKerberos等ではないようです。
>
> なにかおかしいですね。
> UnsafeAuthenticatedConnectionSharingが原因でそのエラーになったことはありません。
> NTLMやKerberosでなければ効果がないだけで、書き込めないことは通常ありません。
>
> 動作してるとはいえ全般に不可解な動作がおおいので、
> インターネット経由であったり、個人情報を扱っているなど、
> セキュリティ上シビアな案件であればきちんと問題を分析しておいた方がいいかもしれません。
>

ご指摘ありがとうございます。

まだ、本格運用するまで間があるので、調査してみます。



引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -