WCF MTOM附件如何设置content-type

WCF MTOM attachment how to set content-type

我需要为 MTOM 配置的绑定中的二进制部分设置特定的内容类型 (application/pdf)。

我可以在配置或代码中设置内容类型吗?对于文件部分,它总是 "Content-Type: application/octet-stream"

配置:

<binding name="FileTransferServicesBinding"
 closeTimeout="00:01:00" openTimeout="00:01:00"
 receiveTimeout="00:10:00" sendTimeout="00:01:00"
 messageEncoding="Mtom" transferMode="Buffered"
 maxBufferSize="67108864" maxReceivedMessageSize="67108864">
 <security mode="Transport" />
</binding>

当前发送到服务的内容。文件的多部分 header 应该是 application/pdf 而不是 application/octetstream

POST https://somewebservice HTTP/1.1
MIME-Version: 1.0
Authorization: Basic authhash
SOAPAction: ""
Host: webservicehost
Content-Length: 24517
Expect: 100-continue
Accept-Encoding: gzip, deflate


--uuid:b8366a06-3ecc-4bc4-9809-8c87ad459981+id=1
Content-ID: <http://tempuri.org/0>
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Header>[The soapmessageitself]</s:Envelope>
--uuid:b8366a06-3ecc-4bc4-9809-8c87ad459981+id=1
Content-ID: <http://tempuri.org/1/636787311873257476>
Content-Transfer-Encoding: binary
Content-Type: application/octet-stream // This must be application/pdf

%PDF-1.4
%    
1 0 obj
<</Type /Catalog/Pages 2 0 R>> ...

我不得不猜测您正在对请求的响应进行编码。如果是这样,您只需将内容类型 header 添加到您的外发消息中:

OperationContext
  .Current
  .OutgoingMessageHeaders
  .Add ("Content-Type", "application/pdf" );

不能 100% 确定,但您可能需要先清除任何现有的传出 Content-Type header。我认为您的消息是 MTOM-encoded 并不重要。理论上是透明的。

另外...只是问...当流式消息传输占用的内存少得多时,为什么允许 64MB 的缓冲消息...而且可能同样快或更快?您几乎可以保证在重负载服务中获得更好的性能。

更新:

如果您有幸使用网络 api-type 绑定 (WebHttpBinding)...您可以非常轻松地流式传输您的内容,指定内容类型。这与我在上面的回答中所写的并没有什么不同,但是正如您所回答的那样,那些 header 去错了地方。

相反,它应该是:

WebOperationContext
  .Current
  .OutgoingResponse
  .ContentType = "application/pdf";

有一个很棒的 article/example here 可以充分发展这个想法...以符合您的目标的方式。

如果您绑定到旧的 soap 绑定,那么您可能需要编写自定义消息编码器。该技术足够简单(如果乏味)。基本思想是您可以控制以适合您需要的方式编写消息的 body。

编码器本身很简单。围绕它的样板将是乏味的部分。自定义编码器源自抽象 MessageEncoder。要做的重要事情是覆盖 ContentTypeMediaTypeMessageVersion 属性以及 ReadMessageWriteMessage 方法。 ReadMessageWriteMessage 方法有多种变体用于处理流式和缓冲消息。

丑陋的部分是您必须编写部署编码器的样板。您已经创建了一个编码器工厂和一个自定义绑定元素。不可怕,但很吵。示例代码包中有几个完整的蜡球示例(包括工厂和绑定元素支持)here...可以节省您一些时间。

随着时间的推移,我通过手动构建 soap 调用解决了这个问题。由于这个问题有赏金,我将标记做相同但使用 WCF 库作为正确答案的答案。下面的代码示例 "byteContent.Headers.Add("Content-Type", "application/pdf; name=test.pdf");"我需要用 WCF

做什么
static HttpClient client;
static object mutex = new object();
static HttpClient Client
{
    get
    {
        if (client == null)
        {
            lock (mutex)
            {
                if (client == null)
                {
                    client = new HttpClient();
                    client.DefaultRequestHeaders.Add("MIME-Version", "1.0");
                    client.DefaultRequestHeaders.Add("SOAPAction", "\"\"");
                    client.DefaultRequestHeaders.ConnectionClose = true;
                    client.DefaultRequestHeaders.ExpectContinue = false;
                    client.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(Username + ":" + Password)));
                }
            }
        }

        return client;
    }
}

public static TResponse Send<TResponse>(string endpoint, string payload, byte[] file)
{
    string boundary = Guid.NewGuid().ToString("N");
    MultipartContent multipart = new MultipartContent("related", boundary);

    multipart.Headers.Remove("Content-Type");
    multipart.Headers.TryAddWithoutValidation("Content-Type", "multipart/related; boundary=\"" + boundary + "\"");
    multipart.Add(new StringContent(payload, Encoding.UTF8, "text/xml"));

    ByteArrayContent byteContent = new ByteArrayContent(file);
    byteContent.Headers.Remove("Content-Type");
    byteContent.Headers.Add("Content-Type", "application/pdf; name=test.pdf");
    byteContent.Headers.Add("Content-Transfer-Encoding", "binary");
    byteContent.Headers.Add("Content-ID", "<test.pdf>");
    byteContent.Headers.Add("Content-Disposition", "attachment; name=\"test.pdf\"; filename=\"test.pdf\"");

    multipart.Add(byteContent);

    var result = Client.PostAsync(endpoint, multipart).Result;
    result.EnsureSuccessStatusCode();

    var resultString = result.Content.ReadAsStringAsync().Result;
    TResponse resultTyped = XmlHelper.ToObject<TResponse>(resultString);
    return resultTyped;
}