4

私はちょっとここで立ち往生しています...

私の目標は非常に単純です。ストリーミングを使用してファイルをアップロードできるIISホスト(およびWindows Azure)WCFサービスを公開し、アップロードするファイル(ファイル名、MD5-ハッシュすべて)に関するいくつかのMETAデータを追加します。通常のもの...)、およびアップロードに関する正確な進捗情報を表示できるようにします。

まず、FileStreamから継承する派生クラスStreamWithProgressを作成しました。ここで、 Readメソッドをオーバーライドして、進行状況情報を渡す各読み取りでイベントを発生させました。

次に、 MessageContracthttp://msdn.microsoft.com/en-us/library/ms730255.aspx )を使用してWCFサービスを作成し、METAデータとストリームオブジェクトを単一のSOAPエンベロープにラップしました。このサービスは本当にシンプルで、アップロードの方法は1つだけです。

次のように、大量のデータを受け入れるようにすべてのバッファサイズを設定しました。

およびhttpRuntime設定は次のとおりです。

次のようなIIS\ASP互換性設定。

そして、次のようにバッチ処理を無効にします。

アップロードが成功するセルフホストサービスを作成しました。次に、それを(ローカルマシン上の)IISホストサービスに「アップグレード」しました。これは機能しました。次に、ローカルでホストされるWindows Azureサービスを、WCFWebロールを使用して作成しました。これは機能しました。

ただし、実際のストリーミングが行われなかったインスタンスはありませんでした…すべてのインスタンスがデータを送信する前にバッファリングしました。

クライアントが進行状況を報告しているのを見たときにこの問題に遭遇しましたが、ファイル全体がバッファリングされるまでサーバーはファイルの書き込みを開始しません。

私の実際のコードは次のとおりです。

何かアイデア\ヘルプ?何でも大歓迎です…</p>

ありがとう!

サーバーweb.config:

<?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>

サービス契約:

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);
    }
}

実際のサービス:

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();
        }
    }
}

クライアントapp.config:

<?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>

クライアントメインコード:

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());
        }
    }
}

派生FileStreamクラス(StreamWithProgress

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
    }
}

-更新:2012-04-20

ループバックアダプターをインストールした後、RawCapを使用して通信をトレースし、データが実際にストリーミングされていることを確認しましたが、IISサーバーはWebメソッドを呼び出す前にすべてのデータをバッファリングしています。

この投稿によると:

http://social.msdn.microsoft.com/Forums/is/wcf/thread/cfe625b2-1890-471b-a4bd-94373daedd39

WCFが継承するのはASP.Netの動作です...しかし、.Net 4.5での修正について話し合っています:|

誰かが他の提案を持っているなら、それは素晴らしいでしょう!

ありがとう!!

4

2 に答える 2

3

Mtomをストリーム転送モードと一緒に使用しています。これにより問題が発生する可能性があります。Mtomを削除してみてください。実際、Mtomはとにかく非常に古い標準です。さらに、ストリーム転送モードでSOAPサービスを使用する場合、タイプがStreamであるパラメーターは1つだけにすることができます。Encapsulatorのようなカスタムタイプは使用できません。

ファイルのアップロード/ダウンロードサービスを構築するための推奨される解決策は、RESTを使用することです。.NETプラットフォームでRESTサービスを構築する方法の1つは、ASP.NET Web API(http://www.asp.net/web-api )を使用することです。このAPIを使用すると、ストリーミング転送モードを処理する必要はありません。処理する必要があるのはRangeヘッダーです。このブログ投稿が役立つ場合があります:http://blogs.msdn.com/b/codefx/archive/2012/02/23/more-about-rest-file-upload-download-service-with-asp-net-web- api-and-windows-phone-background-file-transfer.aspx。ただし、このAPIはまだリリースされていないことにも注意してください。プレリリース製品を使用したくない場合は、他のテクノロジを使用できます。たとえば、MVCコントローラーをRESTサービスとして使用したり、WCF RESTサービスを使用したり、カスタムhttpハンドラーを構築したりできます。必要に応じてStreamを使用するには、カスタムストリームが必要です。サンプルについては、http://blogs.msdn.com/b/james_osbornes_blog/archive/2011/06/10/streaming-with-wcf-part-1-custom-stream-implementation.aspxを確認することをお勧めします。 。

よろしくお願いします、

明徐。

于 2012-04-20T07:30:49.573 に答える
3

いくつかの厳密なテストの結果、データが実際にストリーミングされていることがわかりました。この属性をEncapsulatorクラスに適用し( http://msdn.microsoft.com/en-us/library/ms733742.aspx[MessageContract]に従って)、ファイルに関する追加のメタデータを送信できるようになりました。WireSharkとRawCapを使用すると、ストリームの読み取り中にデータがネットワーク経由で送信されたことは明らかです。

彼の頭を悩ませたもう1つの問題は、アップロードメソッドが実際に呼び出される前に、ストリーミングされるデータがサーバー側で(IIS 7.5を使用して)バッファリングされることでした。これは少し心配ですが、これによると:http ://social.msdn.microsoft.com/Forums/is/wcf/thread/cfe625b2-1890-471b-a4bd-94373daedd39 、修正は4.5にあるはずです.Netのリリース

于 2012-04-24T14:02:40.457 に答える