WCF は、ストリーミングとバッファリングの 2 つのアップロード方法をサポートしています。Buffered はデフォルトのモードで、ファイル全体をバッファリングし、1 つの大きなチャンクでサーバーに送信します。これは、小規模から中規模のファイルではうまく機能しますが、大きなファイルでは遅すぎる傾向があります。ストリーミングは、サーバーへの多数の応答を介してファイルのビットを送信し、ストリームが中断された場合にストリームを再開する機能など、多くの利点があります。
明らかに、この問題に適切な方法はストリーミングですが、ストリーミングはデフォルトではないため、WCF でストリーミングを使用するには、いくつかの構成作業を行う必要があります。ストリーミングを有効にするには Web.config を変更する必要があるため、この回答は特に WCF サービスに適用されます。
まず、ストリームを受け入れる公開メソッドが 1 つあるとします。たとえば、次のようになります。
[OperationContract]
[WebInvoke(UriTemplate = "/Upload", Method = "POST")]
void Upload(Stream data);
このエンドポイントにストリーミングを使用するように WCF に指示する必要があります。これを行うには、ストリーミングを許可するバインディングが必要です。Web.config で次のようなものを使用できます。
<configuration>
...
<bindings>
<webHttpBinding>
<binding name="httpStreamingBinding" transferMode="Streamed" />
</webHttpBinding>
</bindings>
...
<services>
<service name="MyServiceNamespace.MyServiceName">
<endpoint address="" behaviorConfiguration="web" binding="webHttpBinding"
bindingConfiguration="httpStreamingBinding" name="UploadEndpoint"
contract="MyServiceNamespace.IMyServiceName" />
</service>
</services>
...
<behaviors>
...
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
</configuration>
これにより、Upload メソッドを含むクラスを指すエンドポイントが作成され、ストリーミング転送を使用するように構成されます。しかし、まだ問題があります。大きなファイルの転送には時間がかかります。また、Web サーバーの既定のタイムアウトが小さすぎて大きなファイルを転送できないため、Web.config をさらに変更する必要があります。
最初に、httpStreamingBinding のタイムアウトの長さと最大受信メッセージ サイズを次のように変更する必要があります (maxRecievedMessageSize はバイト単位です)。
<binding name="httpStreamingBinding" maxReceivedMessageSize="4294967296"
transferMode="Streamed"
crossDomainScriptAccessEnabled="true"
openTimeout="00:01:00"
closeTimeout="00:01:00"
receiveTimeout="02:00:00"
sendTimeout="02:00:00"
/>
次に、大きなファイルを受け入れるように http ランタイムを変更する必要があります。ここでは、最大値として 4 GB を選択しました (maxRequestLength は kb 単位)。
<system.web>
...
<httpRuntime
executionTimeout="7200"
maxRequestLength="4194304" />
</system.web>
これでストリーミング データを受信できるようになりましたが、まだやるべきことがあります。通常、大規模な HTTP アップロードは multipart/form-data コンテンツ タイプを使用して行われます。これは、ストリームで受信するデータが、アップロードされたファイルだけでなく、フォームからの追加データであることを意味します。このデータを手動で解析することも、既存のパーサーを使用することもできます。Lorenzo は、この回答で優れたマルチパート データ パーサーを提供しています。
マルチパート データのアップロードは HTML フォームを使用すると簡単ですが、JavaScript を使用したい場合はどうすればよいでしょうか。ブラウザーに応じて、ストリーミング データのアップロードにはさまざまなレベルのサポートがありますが、jQuery ファイル アップローダー プラグインは、ストリーミング データ サービスにファイルをアップロードするための優れたサポートを提供します。次のように、マルチパート パーサーのファイル名として「files[]」を必ず使用してください。
[OperationContract]
[WebInvoke(UriTemplate = "/Upload", Method = "POST")]
void Upload(Stream data)
{
var parser = new HttpMultipartParser(data, "files[]");
...
}
データを取得したので、アップロードされたファイルのタイプに基づいて何らかのロジックを実行する方法が必要です。URL パラメータに基づいて解析方法を変更することでこれを解決しました。たとえば、/Upload/FormatOne は FormatOne メソッドを使用し、/Upload/FormatTwo は FormatTwo を使用します。これは、次の方法を使用して実現されます。
delegate void FileFormatHandler(Stream data);
[OperationContract]
[WebInvoke(UriTemplate = "/Upload/{fileType}", Method = "POST")]
void Upload(string fileType, Stream data)
{
var parser = new HttpMultipartParser(data, "files[]");
FileFormatHandler handler = selectHandler(fileType);
handler(data);
}
残念ながら、このメソッドは、Stream が SOAP の唯一の引数であると想定されているため、サービスが SOAP ベースの WCF 呼び出しメカニズムで機能しなくなることを意味しますが、Web 呼び出しはこの制限の影響を受けません。
これで、ストリーミングを使用して大きなファイルを受け入れ、ユーザーが使用する URL に基づいてさまざまな解析メソッドを呼び出すことができる WCF サービスができました。データをアップロードするには、ユーザーは適切な URL に情報を POST するだけです。たとえば、ユーザーがファイルをアップロードして MyFancyFormat パーサーで解析したい場合、次の URL に POST します。
http://myserver/MyService.svc/Upload/MyFancyFormat