WCF - 通过 http 的流式文件上传

发布于 2024-12-06 09:06:25 字数 4007 浏览 4 评论 0原文

我正在尝试构建一个 WCF 服务,该服务将允许我的 WPF 桌面客户端将文件上传到服务器。

我改编了代码项目中的代码示例(WCF Streaming:通过 HTTP 上传/下载文件),我也看过几篇 SO 帖子,但似乎无法正常工作。

当我执行代码时,在服务器尝试读取通过接口传递的流时,它会失败并出现空引用异常。

此时,我很失落,不知道如何解决这个问题。任何建议表示赞赏。

代码示例如下:

CustomerDocumentModel 是我使用流通过 WCF 接口传递以读取客户端文件的数据元素:

[DataContract]
[KnownType(typeof(System.IO.FileStream))]
public class CustomerDocumentModel : IDisposable
{
    public CustomerDocumentModel()
    {
    }

    public CustomerDocumentModel(string documentName, string path)
    {
        DocumentName = documentName;
        Path = path;
    }

    [DataMember]
    public string DocumentName;

    [DataMember]
    public string Path;

    [DataMember]
    public System.IO.Stream FileByteStream;

    public void Dispose()
    { 
        if (FileByteStream != null)
        {
            FileByteStream.Close();
            FileByteStream = null;
        }
    }
}

IBillingService 是我的 WCF 服务的接口定义:

[ServiceContract]
public interface IBillingService
{
    // other methods redacted...

    [OperationContract]
    void UploadCustomerDocument(CustomerDocumentModel model);
}

BillingService 类实现 WCF 服务:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class BillingService : IBillingService
{
    // Other methods redacted ...

    public void UploadCustomerDocument(CustomerDocumentModel model)
    {
        string path = HttpContext.Current.Server.MapPath(
            String.Format("/Documents/{1}",
                model.DocumentName));

        using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
        {
            const int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];

            int size = 0;
            try
            {
                // The following Read() fails with a NullReferenceException
                while ((size = model.FileByteStream.Read(buffer, 0, bufferSize)) > 0)
                {
                    stream.Write(buffer, 0, size);
                }
            }
            catch
            {
                throw;
            }
            finally
            {
            stream.Close();
            model.FileByteStream.Close();
            }
        }
    }
}

中的一些相关位我的 WCF Web 服务器上的 web.config:

<system.web>
    <compilation debug="true" targetFramework="4.0" />
    <httpRuntime maxRequestLength="2097151" useFullyQualifiedRedirectUrl="true" executionTimeout="360"/>
</system.web>

<system.serviceModel>
    <serviceHostingEnvironment
        aspNetCompatibilityEnabled="true"
        multipleSiteBindingsEnabled="true" />
    <bindings>
        <basicHttpBinding>
            <binding name="userHttps" transferMode="Streamed" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
                <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
                <security mode="None" />
            </binding>
        </basicHttpBinding>
    </bindings>
    <behaviors>
        <serviceBehaviors>
            <behavior name="">
                <dataContractSerializer maxItemsInObjectGraph="2147483646"/>
                <serviceDebug includeExceptionDetailInFaults="true" />
                <serviceMetadata httpGetEnabled="true" />
            </behavior>
        </serviceBehaviors>
    </behaviors>
</system.serviceModel>

客户端是一个 WPF/MVVM 应用程序,它创建 CustomerDocumentModel 模型,使用 OpenFileDialog 来 Open() 文件流,然后将模型传递给WCF 服务上的 UploadCustomerDocument 方法。

如果我遗漏任何相关细节,请询问。

I am trying to build a WCF service that will allow my WPF desktop clients to upload files to a server.

I adapted a code sample from The Code Project (WCF Streaming: Upload/Download Files Over HTTP) and I've looked at several SO posts as well, but can't seem to get this working.

When I execute the code, it fails with a null reference exception at the point that the server tries to read the stream that has been passed through the interface.

At this point, I am rather lost and don't know how to fix this up. Any suggestions are appreciated.

Code samples follow:

CustomerDocumentModel is the data element that I pass through the WCF interface with the stream to read the client side file:

[DataContract]
[KnownType(typeof(System.IO.FileStream))]
public class CustomerDocumentModel : IDisposable
{
    public CustomerDocumentModel()
    {
    }

    public CustomerDocumentModel(string documentName, string path)
    {
        DocumentName = documentName;
        Path = path;
    }

    [DataMember]
    public string DocumentName;

    [DataMember]
    public string Path;

    [DataMember]
    public System.IO.Stream FileByteStream;

    public void Dispose()
    { 
        if (FileByteStream != null)
        {
            FileByteStream.Close();
            FileByteStream = null;
        }
    }
}

IBillingService is the interface definition for my WCF service:

[ServiceContract]
public interface IBillingService
{
    // other methods redacted...

    [OperationContract]
    void UploadCustomerDocument(CustomerDocumentModel model);
}

The class BillingService implements the WCF service:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class BillingService : IBillingService
{
    // Other methods redacted ...

    public void UploadCustomerDocument(CustomerDocumentModel model)
    {
        string path = HttpContext.Current.Server.MapPath(
            String.Format("/Documents/{1}",
                model.DocumentName));

        using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
        {
            const int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];

            int size = 0;
            try
            {
                // The following Read() fails with a NullReferenceException
                while ((size = model.FileByteStream.Read(buffer, 0, bufferSize)) > 0)
                {
                    stream.Write(buffer, 0, size);
                }
            }
            catch
            {
                throw;
            }
            finally
            {
            stream.Close();
            model.FileByteStream.Close();
            }
        }
    }
}

A few relevant bits from the web.config on my WCF web server:

<system.web>
    <compilation debug="true" targetFramework="4.0" />
    <httpRuntime maxRequestLength="2097151" useFullyQualifiedRedirectUrl="true" executionTimeout="360"/>
</system.web>

<system.serviceModel>
    <serviceHostingEnvironment
        aspNetCompatibilityEnabled="true"
        multipleSiteBindingsEnabled="true" />
    <bindings>
        <basicHttpBinding>
            <binding name="userHttps" transferMode="Streamed" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
                <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
                <security mode="None" />
            </binding>
        </basicHttpBinding>
    </bindings>
    <behaviors>
        <serviceBehaviors>
            <behavior name="">
                <dataContractSerializer maxItemsInObjectGraph="2147483646"/>
                <serviceDebug includeExceptionDetailInFaults="true" />
                <serviceMetadata httpGetEnabled="true" />
            </behavior>
        </serviceBehaviors>
    </behaviors>
</system.serviceModel>

The client is a WPF/MVVM app that creates a CustomerDocumentModel model, uses an OpenFileDialog to Open() the file stream and then passes the model to the UploadCustomerDocument method on WCF Service.

If I am missing any relevant details, please ask.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

妖妓 2024-12-13 09:06:25

我知道你的问题的答复相当晚了,我相信你也一定已经解决了你的问题。这可能对其他人有帮助:-)

使用 Messagecontract 而不是 Datacontract,并且只有一个数据类型为 Stream 的 MessageBodyMember,其余所有参数都是 MessageHeader。
以下是示例:

[MessageContract]

    public class CustomerDocumentModel : IDisposable
    {

        public CustomerDocumentModel(string documentName, string path)
        {
            DocumentName = documentName;
            Path = path;
        }

        [MessageHeader]
        public string DocumentName{get;set;}

        [MessageHeader]
        public string Path{get;set;}

        [MessageBodyMember]
        public System.IO.Stream FileByteStream{get;set;}

        public void Dispose()
        { 
            if (FileByteStream != null)
            {
                FileByteStream.Close();
                FileByteStream = null;
            }
        }
    }

注意:确保您的配置传输模式是 StreamedResponse,并且您可能希望将 MessageEncoding 更改为 MTOM 以获得更好的性能。

public void UploadCustomerDocument(CustomerDocumentModel model)
{
        var filename = //your file name and path;
        using (var fs = new FileStream(filename, FileMode.Create))

        {
               model.FileByteStream.CopyTo(fs);
        }
}

I know this rather very late reply for your question and I'm sure you must have resolved your problem as well. This could be helpful to someone else :-)

Use Messagecontract over Datacontract and only one MessageBodyMember with datatype Stream and rest all parameter are MessageHeader.
Here is the example:

[MessageContract]

    public class CustomerDocumentModel : IDisposable
    {

        public CustomerDocumentModel(string documentName, string path)
        {
            DocumentName = documentName;
            Path = path;
        }

        [MessageHeader]
        public string DocumentName{get;set;}

        [MessageHeader]
        public string Path{get;set;}

        [MessageBodyMember]
        public System.IO.Stream FileByteStream{get;set;}

        public void Dispose()
        { 
            if (FileByteStream != null)
            {
                FileByteStream.Close();
                FileByteStream = null;
            }
        }
    }

Note: Make sure your in your configuration transfer mode is StreamedResponse, also you may want to change the MessageEncoding to MTOM for better performance.

public void UploadCustomerDocument(CustomerDocumentModel model)
{
        var filename = //your file name and path;
        using (var fs = new FileStream(filename, FileMode.Create))

        {
               model.FileByteStream.CopyTo(fs);
        }
}
烟酒忠诚 2024-12-13 09:06:25

您的数据类型是导致流式传输失败的原因。 MSDN 上对此进行了记录:http://msdn.microsoft.com/en-我们/library/ms731913.aspx
相关段落是:

流式传输的限制

使用流式传输模式会导致运行时间强制执行
额外的限制。

跨流传输发生的操作可以有一个契约
至多有一个输入或输出参数。该参数对应
消息的整个正文,并且必须是消息,派生的
Stream 类型,或 IXmlSerialized 实现。有回报
操作的值相当于有一个输出参数。

一些 WCF 功能,例如可靠消息传递、事务和 SOAP
消息级安全性,依赖于缓冲消息进行传输。
使用这些功能可能会减少或消除性能优势
通过使用流媒体获得。要保护流传输,请使用
仅传输级安全性或使用传输级安全性加
仅身份验证消息安全性。

即使设置了传输模式,SOAP 标头也始终被缓冲
进行流式传输。消息头的大小不能超过
MaxBufferSize 传输配额。有关此的更多信息
设置,请参阅传输配额。

Your data type is what is making the streaming fail. This is documented on MSDN here: http://msdn.microsoft.com/en-us/library/ms731913.aspx
The relevant passage is:

Restrictions on Streamed Transfers

Using the streamed transfer mode causes the run time to enforce
additional restrictions.

Operations that occur across a streamed transport can have a contract
with at most one input or output parameter. That parameter corresponds
to the entire body of the message and must be a Message, a derived
type of Stream, or an IXmlSerializable implementation. Having a return
value for an operation is equivalent to having an output parameter.

Some WCF features, such as reliable messaging, transactions, and SOAP
message-level security, rely on buffering messages for transmissions.
Using these features may reduce or eliminate the performance benefits
gained by using streaming. To secure a streamed transport, use
transport-level security only or use transport-level security plus
authentication-only message security.

SOAP headers are always buffered, even when the transfer mode is set
to streamed. The headers for a message must not exceed the size of the
MaxBufferSize transport quota. For more information about this
setting, see Transport Quotas.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文