WCF 流式 PDF 响应

WCF Streamed PDF Response

我的目标是return一个PDF文件流返回给客户端。

所以在 WCF 方面我有:

public interface IPersonalPropertyService
    {
        [OperationContract]
        Stream GetQuotation();
    }

public class PersonalPropertyService : IPersonalPropertyService
    { 
        public Stream GetQuotation()
        {
            var filePath = HostingEnvironment.ApplicationPhysicalPath + @"Quotation.pdf";
            var fileInfo = new FileInfo(filePath);
            // check if exists
            if (!fileInfo.Exists)
                throw new FileNotFoundException("File not found");
            FileStream stm = File.Open(filePath, FileMode.Open);
            WebOperationContext.Current.OutgoingResponse.ContentType = "application/pdf";
            return stm;  
        } 
    }

配置部分如下:

<system.serviceModel>
    <client>
      <endpoint
        binding="basicHttpBinding"
        bindingConfiguration="StreamedHttp"
        contract="IPersonalPropertyService" >
      </endpoint>
    </client>
    <bindings>
      <basicHttpBinding>
        <binding name="StreamedHttp" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"
          transferMode="Streamed">
          <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
            maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
        </binding>
      </basicHttpBinding>
    </bindings>

    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

现在在客户端(例如控制台应用程序),当我创建服务引用时,我希望看到我的 basicHttpBinding StreamedHttp 配置,但生成了以下配置:

<system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_PersonalPropertyService" />
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="MyPath/PersonalPropertyService.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_PersonalPropertyService"
                contract="TheNamespace.ServiceReference.PersonalPropertyService"
                name="BasicHttpBinding_PersonalPropertyService" />
        </client>
    </system.serviceModel>

而且我认为由于这个原因,我收到了 ProtocolException 异常

The content type application/pdf of the response message does not match the content type of the binding (text/xml; charset=utf-8).

如何强制客户端接受在 WCF 端定义的流配置?

谢谢

这是一个经过测试的控制台应用程序。只需在您的可执行文件中创建 "Files" 目录,将您的文件放入其中,然后将类似 http://localhost:8088/fileServer/a.pdf 的内容粘贴到您的浏览器。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;
using System.Text;
using System.Web;

namespace SampleWCFConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            FileServer.Start();
        }
    }

    [ServiceContract]
    public class FileServer
    {
        static WebServiceHost _Host;
        public static void Start()
        {
            _Host = new WebServiceHost(typeof(FileServer), new Uri("http://0.0.0.0:8088/FileServer"));
            _Host.Open();
            Console.ReadLine();
        }

        [OperationContract, WebGet(UriTemplate = "*")]
        public Message Get()
        {
            var ctx = WebOperationContext.Current;

            var fileName = Path.Combine("Files", String.Join("/", WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RelativePathSegments));

            var fInfo = new FileInfo(fileName);

            var eTag = ctx.IncomingRequest.Headers[HttpRequestHeader.IfNoneMatch];
            if (!string.IsNullOrEmpty(eTag))
            {
                if (GetETag(fInfo) == eTag)
                {
                    ctx.OutgoingResponse.StatusCode = HttpStatusCode.NotModified;
                    return ctx.CreateTextResponse("");
                }
            }

            if (fInfo.Exists == false) return ReturnError(ctx, HttpStatusCode.NotFound);
            return ReturnFile(ctx, fInfo);
        }

        static string GetETag(FileInfo fInfo)
        {
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(fInfo.Name).Concat(BitConverter.GetBytes(fInfo.LastWriteTime.Ticks)).ToArray());
        }

        public static Message ReturnError(WebOperationContext ctx, HttpStatusCode statusCode)
        {
            ctx.OutgoingResponse.StatusCode = statusCode;
            return ctx.CreateTextResponse(statusCode.ToString(), "text/html");
        }

        static Message ReturnFile(WebOperationContext ctx, FileInfo fInfo, HttpStatusCode statusCode = HttpStatusCode.OK)
        {
            ctx.OutgoingResponse.StatusCode = statusCode;
            ctx.OutgoingResponse.ETag = GetETag(fInfo);

            return ctx.CreateStreamResponse(File.OpenRead(fInfo.FullName), MimeMapping.GetMimeMapping(fInfo.Name));
        }
    }
}

顺便说一句:如果你愿意,你可以删除 if-not-modified 代码,如果客户端 uptodate版本。