作法:啟用資料流
Windows Communication Foundation (WCF) 可以透過緩衝處理或串流處理的傳輸來傳送訊息。 在預設的緩衝傳輸模式中,必須完整傳遞訊息,接收者才能讀取。 在資料流傳輸模式中,接收者不需等到訊息完全送達,就可以開始處理訊息。 當資訊的傳遞很漫長,但是可依序列處理時,使用資料流模式將十分有幫助。 當訊息太龐大而無法完整加以緩衝時,資料流模式也很有用處。
若要啟用資料流處理,請適當定義 OperationContract
並在傳輸層級啟用資料流處理。
若要以資料流方式處理資料
若要以資料流方式處理資料,服務的
OperationContract
必須滿足兩項需求:用來存放要進行資料流處理之資料的參數,必須是方法中的唯一參數。 例如,如果輸入訊息是要處理成資料流的訊息,這項處理作業就必須剛好只有一個輸入參數。 同樣地,如果要將輸出訊息處理成資料流,這項作業也必須剛好只有一個輸出參數或傳回值。
至少有一個參數型別與傳回值必須是 Stream、Message 或 IXmlSerializable。
下列為資料流處理資料合約的範例。
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")] public interface IStreamingSample { [OperationContract] Stream GetStream(string data); [OperationContract] bool UploadStream(Stream stream); [OperationContract] Stream EchoStream(Stream stream); [OperationContract] Stream GetReversedStream(); }
<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _ Public Interface IStreamingSample <OperationContract()> _ Function GetStream(ByVal data As String) As Stream <OperationContract()> _ Function UploadStream(ByVal stream As Stream) As Boolean <OperationContract()> _ Function EchoStream(ByVal stream As Stream) As Stream <OperationContract()> _ Function GetReversedStream() As Stream End Interface
GetStream
作業會接收一些緩衝處理的輸入資料做為string
(會經過緩衝處理,並傳回已經過資料流處理的Stream
)。 相反地,UploadStream
會接受Stream
(經過資料流處理) 並傳回bool
(經過緩衝處理)。EchoStream
會接受並傳回Stream
,而這項作業也是對輸入和輸出兩種訊息都進行資料流處理的範例。 最後,GetReversedStream
不接受任何輸入,而只是傳回Stream
(經過資料流處理)。繫結必須啟用資料流處理。 您可以設定
TransferMode
屬性,並採用下列其中一個值:Buffered
,Streamed
,可啟用雙向資料流通訊。StreamedRequest
,只會啟用要求的資料流處理。StreamedResponse
,只會啟用回應的資料流處理。
BasicHttpBinding
會公開繫結上的TransferMode
屬性,就像NetTcpBinding
和NetNamedPipeBinding
一樣。TransferMode
屬性也可以在傳輸繫結項目上設定,並用於自訂繫結。下列範例說明如何透過程式碼與藉由變更組態檔來設定
TransferMode
。 這些範例同時都會將maxReceivedMessageSize
屬性設為 64 MB,以限制允許接收的最大訊息大小。 預設的maxReceivedMessageSize
是 64 KB,但這對資料流案例來說,通常是過低的。 適當的配額設定取決於您的應用程式預期接收的最大訊息大小。 同時請注意,maxBufferSize
會控制緩衝處理的最大大小,請適當設定。範例中的下列組態片段示範將
TransferMode
屬性設定為會在basicHttpBinding
和自訂 HTTP 繫結上進行資料流處理。<basicHttpBinding> <binding name="HttpStreaming" maxReceivedMessageSize="67108864" transferMode="Streamed"/> </basicHttpBinding> <!-- an example customBinding using Http and streaming--> <customBinding> <binding name="Soap12"> <textMessageEncoding messageVersion="Soap12WSAddressing10" /> <httpTransport transferMode="Streamed" maxReceivedMessageSize="67108864"/> </binding> </customBinding>
下列程式碼片段示範將
TransferMode
屬性設定為會在basicHttpBinding
和自訂 HTTP 繫結上進行資料流處理。public static Binding CreateStreamingBinding() { BasicHttpBinding b = new BasicHttpBinding(); b.TransferMode = TransferMode.Streamed; return b; }
Public Shared Function CreateStreamingBinding() As Binding Dim b As New BasicHttpBinding() b.TransferMode = TransferMode.Streamed Return b End Function
下列程式碼片段示範將
TransferMode
屬性設定為會在自訂 TCP 繫結上進行資料流處理。public static Binding CreateStreamingBinding() { TcpTransportBindingElement transport = new TcpTransportBindingElement(); transport.TransferMode = TransferMode.Streamed; BinaryMessageEncodingBindingElement encoder = new BinaryMessageEncodingBindingElement(); CustomBinding binding = new CustomBinding(encoder, transport); return binding; }
Public Shared Function CreateStreamingBinding() As Binding Dim transport As New TcpTransportBindingElement() transport.TransferMode = TransferMode.Streamed Dim binding As New CustomBinding(New BinaryMessageEncodingBindingElement(), _ transport) Return binding End Function
GetStream
、UploadStream
和EchoStream
都會處理直接從檔案傳送資料或直接將接收的資料儲存至檔案的作業。 下列為GetStream
程式碼。public Stream GetStream(string data) { //this file path assumes the image is in // the Service folder and the service is executing // in service/bin string filePath = Path.Combine( System.Environment.CurrentDirectory, ".\\..\\image.jpg"); //open the file, this could throw an exception //(e.g. if the file is not found) //having includeExceptionDetailInFaults="True" in config // would cause this exception to be returned to the client try { FileStream imageFile = File.OpenRead(filePath); return imageFile; } catch (IOException ex) { Console.WriteLine( String.Format("An exception was thrown while trying to open file {0}", filePath)); Console.WriteLine("Exception is: "); Console.WriteLine(ex.ToString()); throw ex; } }
Public Function GetStream(ByVal data As String) As Stream Implements IStreamingSample.GetStream 'this file path assumes the image is in ' the Service folder and the service is executing ' in service/bin Dim filePath = Path.Combine(System.Environment.CurrentDirectory, ".\..\image.jpg") 'open the file, this could throw an exception '(e.g. if the file is not found) 'having includeExceptionDetailInFaults="True" in config ' would cause this exception to be returned to the client Try Return File.OpenRead(filePath) Catch ex As IOException Console.WriteLine(String.Format("An exception was thrown while trying to open file {0}", filePath)) Console.WriteLine("Exception is: ") Console.WriteLine(ex.ToString()) Throw ex End Try End Function
撰寫自訂資料流
若要對每個正在傳送或接收的資料流區塊 (Chunk) 進行特殊處理,請從 Stream 衍生自訂資料流類別。 下列程式碼包含
GetReversedStream
方法和ReverseStream
類別,是一個自訂資料流範例。GetReversedStream
會建立並傳回ReverseStream
的新執行個體。 實際的處理會在系統從這個ReverseStream
物件讀取時進行。ReverseStream.Read
方法會從基礎檔案讀取位元組區塊,然後將其序列反轉,再傳回此相反序列的位元組。 此方法不會反轉整個檔案內容,它只是一次反轉一個位元組區塊。 下列範例說明如何在對資料流讀取內容或寫入內容時執行資料流處理。class ReverseStream : Stream { FileStream inStream; internal ReverseStream(string filePath) { //opens the file and places a StreamReader around it inStream = File.OpenRead(filePath); } public override bool CanRead { get { return inStream.CanRead; } } public override bool CanSeek { get { return false; } } public override bool CanWrite { get { return false; } } public override void Flush() { throw new Exception("This stream does not support writing."); } public override long Length { get { throw new Exception("This stream does not support the Length property."); } } public override long Position { get { return inStream.Position; } set { throw new Exception("This stream does not support setting the Position property."); } } public override int Read(byte[] buffer, int offset, int count) { int countRead = inStream.Read(buffer, offset, count); ReverseBuffer(buffer, offset, countRead); return countRead; } public override long Seek(long offset, SeekOrigin origin) { throw new Exception("This stream does not support seeking."); } public override void SetLength(long value) { throw new Exception("This stream does not support setting the Length."); } public override void Write(byte[] buffer, int offset, int count) { throw new Exception("This stream does not support writing."); } public override void Close() { inStream.Close(); base.Close(); } protected override void Dispose(bool disposing) { inStream.Dispose(); base.Dispose(disposing); } void ReverseBuffer(byte[] buffer, int offset, int count) { int i, j; for (i = offset, j = offset + count - 1; i < j; i++, j--) { byte currenti = buffer[i]; buffer[i] = buffer[j]; buffer[j] = currenti; } } }
Friend Class ReverseStream Inherits Stream Private inStream As FileStream Friend Sub New(ByVal filePath As String) 'opens the file and places a StreamReader around it inStream = File.OpenRead(filePath) End Sub Public Overrides ReadOnly Property CanRead() As Boolean Get Return inStream.CanRead End Get End Property Public Overrides ReadOnly Property CanSeek() As Boolean Get Return False End Get End Property Public Overrides ReadOnly Property CanWrite() As Boolean Get Return False End Get End Property Public Overrides Sub Flush() Throw New Exception("This stream does not support writing.") End Sub Public Overrides ReadOnly Property Length() As Long Get Throw New Exception("This stream does not support the Length property.") End Get End Property Public Overrides Property Position() As Long Get Return inStream.Position End Get Set(ByVal value As Long) Throw New Exception("This stream does not support setting the Position property.") End Set End Property Public Overrides Function Read(ByVal buffer() As Byte, _ ByVal offset As Integer, _ ByVal count As Integer) As Integer Dim countRead = inStream.Read(buffer, _ offset, _ count) ReverseBuffer(buffer, _ offset, _ countRead) Return countRead End Function Public Overrides Function Seek(ByVal offset As Long, _ ByVal origin As SeekOrigin) As Long Throw New Exception("This stream does not support seeking.") End Function Public Overrides Sub SetLength(ByVal value As Long) Throw New Exception("This stream does not support setting the Length.") End Sub Public Overrides Sub Write(ByVal buffer() As Byte, _ ByVal offset As Integer, _ ByVal count As Integer) Throw New Exception("This stream does not support writing.") End Sub Public Overrides Sub Close() inStream.Close() MyBase.Close() End Sub Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) inStream.Dispose() MyBase.Dispose(disposing) End Sub Private Sub ReverseBuffer(ByVal buffer() As Byte, _ ByVal offset As Integer, _ ByVal count As Integer) Dim i = offset Dim j = offset + count - 1 Do While i < j Dim currenti = buffer(i) buffer(i) = buffer(j) buffer(j) = currenti i += 1 j -= 1 Loop End Sub End Class