Начало работы потоковой передачи в IIS Hosted WCF
Я застрял здесь ...
Моя цель довольно проста: я хочу предоставить службу WCF, размещенную на IIS (а затем и Windows Azure), с помощью которой я могу загружать файлы, используя потоковую передачу, и добавлять некоторые данные META о файле, который я хочу загрузить (имя файла, MD5-хэш все обычные вещи ...), и чтобы иметь возможность отображать точную информацию о ходе загрузки.
Прежде всего я создал производный классStreamWithProgress который наследует отFileStreamгде я переопределилRead способ вызвать событие с каждым прочитанным, через который я передаю информацию о прогрессе.
Во-вторых, я создал службу WCF, используяMessageContract ( http://msdn.microsoft.com/en-us/library/ms730255.aspx ), чтобы обернуть данные META и объект потока в один конверт SOAP. Этот сервис действительно прост, предоставляя только один метод для загрузки.
Я установил все размеры буфера, чтобы принимать большие объемы данных, согласно:
http://msdn.microsoft.com/en-us/library/ms733742.aspx and
http://msdn.microsoft.com/en-us/library/ms731325.aspx
и настройки httpRuntime согласно:
http://msdn.microsoft.com/en-us/library/e1f13641(v=vs.71).aspx and
http://kjellsj.blogspot.com/2007/02/wcf-streaming-upload-files-over-http.html
параметры совместимости IIS \ ASP в соответствии с:
http://weblogs.asp.net/jclarknet/archive/2008/02/14/wcf-streaming-issue-under-iis.aspx and
WCF Streaming File Transfer ON .NET 4
И отключение дозирования в соответствии с:
http://msdn.microsoft.com/en-us/library/system.servicemodel.icontextchannel.allowoutputbatching.aspxЯ создалself hosted service через который загрузка прошла успешно. Затем я обновил & # x2019; это кIIS hosted service (на моей местной машине), которая работала. Затем я создалlocally hosted Windows Azure service, with a WCF webrole, который работал.
Однако подвох состоит в том, что ни в одном из случаев не происходило потоковое вещание & # x2026; Все они буферизовали данные перед отправкой.
Я столкнулся с этой проблемой, когда увидел, что мой клиент сообщает о прогрессе, но сервер не начинает запись файла до тех пор, пока весь файл не будет помещен в буфер.
Мой фактический код следует.
Любые идеи \ помощь? Все будет высоко оценено & # x2026;
Спасибо!
Сервер web.config:
<code><?xml version="1.0"?> <configuration> <system.serviceModel> <bindings> <basicHttpBinding> <binding name="uploadBasicHttpBinding" maxReceivedMessageSize="2147483647" transferMode="Streamed" messageEncoding="Mtom" maxBufferPoolSize="2147483647" maxBufferSize="2147483647"> <readerQuotas maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxDepth="2147483647" maxNameTableCharCount="2147483647" maxStringContentLength="2147483647"/> </binding> </basicHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="defaultBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="false"/> <dataContractSerializer maxItemsInObjectGraph="2147483647"/> </behavior> </serviceBehaviors> </behaviors> <!-- Add this for BufferOutput setting --> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true"/> <services> <service name="WcfService1.Service1" behaviorConfiguration="defaultBehavior"> <endpoint binding="basicHttpBinding" contract="WcfService1.IService1" bindingConfiguration="uploadBasicHttpBinding"/> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> </system.serviceModel> <system.webServer> <modules runAllManagedModulesForAllRequests="true"/> </system.webServer> <system.web> <compilation debug="true"/> <httpRuntime maxRequestLength="2147483647" /> </system.web> </configuration> </code>
Контракт на обслуживание:
<code>using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; using System.IO; namespace WcfService1 { [ServiceContract] public interface IService1 { [OperationContract(IsOneWay=true)] void UploadStream(Encapsulator data); } } </code>
Актуальное обслуживание:
<code>using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; using System.IO; using System.Web; using System.ServiceModel.Activation; namespace WcfService1 { [MessageContract] public class Encapsulator { [MessageHeader(MustUnderstand = true)] public string fileName; [MessageBodyMember(Order = 1)] public Stream requestStream; } [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class Service1 : IService1 { public Service1() { HttpContext context = HttpContext.Current; if (context != null) { context.Response.BufferOutput = false; } } public void UploadStream(Encapsulator data) { const int BUFFER_SIZE = 1024; int bytesRead = 0; byte[] dataRead = new byte[BUFFER_SIZE]; string filePath = Path.Combine(@"C:\MiscTestFolder", data.fileName); string logPath = Path.Combine(@"C:\MiscTestFolder", string.Concat(data.fileName, ".log")); bytesRead = data.requestStream.Read(dataRead, 0, BUFFER_SIZE); StreamWriter logStreamWriter = new StreamWriter(logPath); using (System.IO.FileStream fileStream = new System.IO.FileStream(filePath, FileMode.Create)) { while (bytesRead > 0) { fileStream.Write(dataRead, 0, bytesRead); fileStream.Flush(); logStreamWriter.WriteLine("Flushed {0} bytes", bytesRead.ToString()); logStreamWriter.Flush(); bytesRead = data.requestStream.Read(dataRead, 0, BUFFER_SIZE); } fileStream.Close(); } logStreamWriter.Close(); } } } </code>
Клиентский app.config:
<code><?xml version="1.0"?> <configuration> <system.serviceModel> <bindings> <basicHttpBinding> <binding name="BasicHttpBinding_IService1" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Mtom" textEncoding="utf-8" transferMode="Streamed" useDefaultWebProxy="true"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <security mode="None"> <transport clientCredentialType="None" proxyCredentialType="None" realm="" /> <message clientCredentialType="UserName" algorithmSuite="Default" /> </security> </binding> </basicHttpBinding> </bindings> <client> <endpoint address="http://localhost/WcfService1/Service1.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1" contract="UploadService.IService1" name="BasicHttpBinding_IService1" /> </client> </system.serviceModel> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> </startup> </configuration> </code>
Основной код клиента:
<code>using System; using System.Collections.Generic; using System.Linq; using System.Text; using CustomFileUploaderTester.UploadService; using System.ServiceModel; using System.IO; namespace CustomFileUploaderTester { class Program { private static long bytesRead = 0; static void Main(string[] args) { Service1Client client = new Service1Client(); using (StreamWithProgress fstream = new StreamWithProgress(@"C:\BladieBla\someFile.wmv", FileMode.Open)) { client.InnerChannel.AllowOutputBatching = false; fstream.ProgressChange += new EventHandler<StreamReadProgress>(fstream_ProgressChange); client.UploadStream("someFile.wmv", fstream); fstream.Close(); } Console.ReadKey(); } static void fstream_ProgressChange(object sender, StreamReadProgress e) { bytesRead += e.BytesRead; Console.WriteLine(bytesRead.ToString()); } } } </code>
Производный класс FileStream (StreamWithProgress)
<code>using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace CustomFileUploaderTester { public class StreamReadProgress : EventArgs { #region Public Properties public long BytesRead { get; set; } public long Length { get; set; } #endregion #region Constructor public StreamReadProgress(long bytesRead, long fileLength) : base() { this.BytesRead = bytesRead; this.Length = fileLength; } #endregion } public sealed class StreamWithProgress : FileStream { #region Public Events public event EventHandler<StreamReadProgress> ProgressChange; #endregion #region Constructor public StreamWithProgress(string filePath, FileMode fileMode) : base(filePath, fileMode) { } #endregion #region Overrides public override int Read(byte[] array, int offset, int count) { int bytesRead = base.Read(array, offset, count); this.RaiseProgressChanged(bytesRead); return bytesRead; } #endregion #region Private Worker Methods private void RaiseProgressChanged(long bytesRead) { EventHandler<StreamReadProgress> progressChange = this.ProgressChange; if (progressChange != null) { progressChange(this, new StreamReadProgress(bytesRead, this.Length)); } } #endregion } } </code>
-- Update: 2012-04-20
После того, как я установил адаптер обратной связи, я проследил связь с RawCap и увидел, что данные на самом деле передаются в потоковом режиме, но сервер IIS буферизует все данные перед вызовом веб-метода!
Согласно этому посту:
http://social.msdn.microsoft.com/Forums/is/wcf/thread/cfe625b2-1890-471b-a4bd-94373daedd39
это поведение ASP.Net, которое наследует WCF ... Но они говорят об исправлениях этого в .Net 4.5: |
Если у кого-то есть другие предложения, это будет здорово!
Спасибо!!