简体   繁体   English

如何设置附件名称以在Outlook中正确显示

[英]How Set Attachment Name to Show Properly in Outlook

I'm creating an email with a MIME attachment from a BizTalk 2016 SMTP Send Port. 我正在使用BizTalk 2016 SMTP发送端口创建一个带有MIME附件的电子邮件。 However, I think any knowledge that anyone can share from any other language about the oddities of Outlook and MIME might help me fix the issue below. 但是,我认为任何人都可以从任何其他语言中分享有关Outlook和MIME的奇怪之处的任何知识可能会帮助我解决下面的问题。

In Outlook, the attachment shows as body.txt, but when I click "File Save" it shows the name that I used when I created it (and that's what the user wants to see). 在Outlook中,附件显示为body.txt,但是当我单击“文件保存”时,它会显示我在创建它时使用的名称(这是用户想要查看的名称)。

What I'm referring to is the the left side where it says "body.txt" above the 5k and to the right of the attachment icon in the screen shot below: 我所指的是左侧,在5k上方的“body.txt”和下面屏幕截图中附件图标的右侧:

在此输入图像描述

In BizTalk C# Pipeline component, that attachment was set with the following code, where we are setting Context properties on the BizTalk Message. 在BizTalk C#Pipeline组件中,使用以下代码设置该附件,我们在BizTalk消息上设置Context属性。 I also tried setting ContentHeader and ContentID. 我也尝试过设置ContentHeader和ContentID。

strFilename = "MyFileName_693.txt";  // Just for example. 
pInMsg.BodyPart.PartProperties.Write(
              "FileName",
              "http://schemas.microsoft.com/BizTalk/2003/mime-properties",
               strFilename);

When I forwarded the email to my Gmail, the attachment was shown with the proper name. 当我将电子邮件转发到我的Gmail时,附件显示了正确的名称。 So my question is particular to making it appear with the desired name in Outlook (2016). 所以我的问题特别是让它在Outlook(2016)中显示所需的名称。

So far I've got this working with an orchestration with a dynamic send port. 到目前为止,我已经开始使用带有动态发送端口的业务流程。 It's still a bit of work, but it gets the job done with the stock component. 它仍然有点工作,但它完成了库存组件的工作。 Following description is based on the stock SMTP-adapter included in BizTalk 2013R2. 以下描述基于BizTalk 2013R2中包含的库存SMTP适配器。

Note: even though my solution works, it feels like a workaround and something i shouldn't have to do, if the adapter was just slightly smarter about this. 注意:即使我的解决方案有效,感觉就像一个解决方法,我不应该做的事情,如果适配器只是稍微聪明一点。

First of all, let's look example email snippet which causes issues in some clients: 首先,让我们看一下示例电子邮件片段,它会导致某些客户端出现问题:

------=_NextPart_000_0001_01D4502F.8A6A1500
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="utf-8"

See attached email.
------=_NextPart_000_0001_01D4502F.8A6A1500
Content-Type: application/pdf; name="CDM_Order - Copy.pdf"
Content-Disposition: attachment; filename="CDM_Order - Copy.pdf"
Content-Description: body
Content-Transfer-Encoding: base64

JVBERi0xLjQKJeLjz9MNCjUgMCBvYmoKPDwvRFsgMyAwIFIvWFlaIG51bGwgODQxLjg4OTc3IG51
bGwgXQo+PgplbmRvYmoKOCAwIG9iago8PC9EWyAzIDAgUi9YWVogbnVsbCAyOTAuMjM2NTcgbnVs
bCBdCj4+ (etc etc base64 your file)...

Notice the Content-Description: body part. 注意Content-Description: body部分。 This is the reason why some clients read body.xml or in my case body.pdf , even though the Disposition part looks great: Content-Disposition: attachment; filename="CDM_Order - Copy.pdf" 这就是为什么有些客户端读取body.xml或我的body.pdf ,即使Disposition部分看起来很棒: Content-Disposition: attachment; filename="CDM_Order - Copy.pdf" Content-Disposition: attachment; filename="CDM_Order - Copy.pdf" . Content-Disposition: attachment; filename="CDM_Order - Copy.pdf"

Hard setting MIME.FileName isn't just going to work, even though it will set the Content-Disposition right eventually, it'll never update the Content-Description . 硬设置MIME.FileName不仅可以工作,即使它最终会设置Content-Disposition ,它也永远不会更新Content-Description This is because either on a static send port you've set the Attach only body part or you specified the corresponding numeric value 1 on a dynamic send port. 这是因为在静态发送端口上,您已设置Attach only body part或者您在动态发送端口上指定了相应的数字值1

However, it will work with the Attach all parts or 2 value for the type MessagePartsAttachments . 但是,它将与MessagePartsAttachments类型的Attach all parts2值一起使用。 This involves making a multi-part message in your orchestration. 这涉及在业务流程中制作多部分消息。 This will have two parts; 这将有两个部分;

  • First one is the BodyPart , now this one will include your message text and not your attachment. 第一个是BodyPart ,现在这个将包含您的消息文本而不是您的附件。 Make sure you specify this one as Message Body Part in the Message Type . 确保在Message Type将此指定为Message Body Part
  • Second part will be your actual attachment, specify this type according to your attachment type. 第二部分将是您的实际附件,根据您的附件类型指定此类型。 I named this Attachment in this example. 我在这个例子中命名了这个Attachment

Now you might think it will send the BodyPart as attachment as well since i've said we needed Attach all parts . 现在您可能会认为它会将BodyPart作为附件发送,因为我已经说过我们需要Attach all parts This is true, so to correct that, your BodyPart has to be defined as a RawString , this turns the string into plain text in the BizTalk message part. 这是正确的,因此要纠正这一点,必须将BodyPart定义为RawString ,这会将字符串转换为BizTalk消息部分中的纯文本。 For completeness i'll put the C# class at the bottom for reference. 为了完整性,我将把C#类放在底部以供参考。

Now that it's defined as a RawString , the SMTP adapter will put this as the body instead of as attachment. 现在它被定义为RawString ,SMTP适配器将把它作为正文而不是附件。 As a side effect, the SMTP adapter will no longer put the Content-Description: body part in the attachment part, but in the actual body part instead. 作为副作用,SMTP适配器将不再将Content-Description: body部分放在附件部分中,而是放在实际的正文部分中。 It looks like this: 它看起来像这样:

------=_NextPart_000_0001_01D450E4.A7E9A5E0
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="utf-8"
Content-Description: body

See attached email.
------=_NextPart_000_0001_01D450E4.A7E9A5E0
Content-Type: application/pdf; name="ID_0_Nummer_0.pdf"
Content-Disposition: attachment; filename="ID_0_Nummer_0.pdf"
Content-Transfer-Encoding: base64

JVBERi0xLjQKJeLjz9MNCjUgMCBvYmoKPDwvRFsgMyAwIFIvWFlaIG51bGwgODQxLjg4OTc3IG51
bGwgXQo+PgplbmRvYmoKOCAwIG9iago8PC9EWyAzIDAgUi9YWVogbnVsbCAyOTAuMjM2NTcgbnVs
bCBdCj4+ (etc etc base64 your file)...

Really nothing else is different except the placement of the Content-Description: body part, exactly what we want. 除了Content-Description: body部分的位置之外,其他没有什么不同,正是我们想要的。 Now the email looks fine for every client. 现在电子邮件看起来很适合每个客户。

The most important properties, besides the ones i already mentioned, must be set as well to make it behave properly: 除了我已经提到的那些之外,最重要的属性也必须设置为使其行为正常:

Content type of your body: 您身体的内容类型:

MsgPdfOrder.BodyPart(Microsoft.XLANGs.BaseTypes.ContentType) = "text/plain";

Content type of your attachment: 附件的内容类型:

MsgPdfOrder.Attachment(Microsoft.XLANGs.BaseTypes.ContentType) = "application/pdf";

Attachment filename: 附件文件名:

MsgPdfOrder.Attachment(MIME.FileName) =  "CDM_Order - Copy.pdf"

Body character set (will result in Unknown Error Description if not set): 正文字符集(如果未设置,将导致Unknown Error Description ):

MsgPdfOrder(SMTP.EmailBodyTextCharset) = "UTF-8";

Make sure you don't set the SMTP.EmailBodyText because we already have the BodyPart for that. 确保您没有设置SMTP.EmailBodyText因为我们已经有了BodyPart

RawString class, use it like this in an orchestration MsgPdfOrder.BodyPart = new Yournamespace.Components.RawString("See attached email."); RawString类,在业务流程中使用它像MsgPdfOrder.BodyPart = new Yournamespace.Components.RawString("See attached email."); :

using System.Runtime.Serialization;
using System;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using Microsoft.XLANGs.BaseTypes;

namespace Yournamespace.Components
{
    public abstract class BaseFormatter : IFormatter
    {
        public virtual SerializationBinder Binder
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }

        public virtual StreamingContext Context
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }

        public virtual ISurrogateSelector SurrogateSelector
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }

        public abstract void Serialize(Stream stm, object obj);
        public abstract object Deserialize(Stream stm);
    }

    public class RawStringFormatter : BaseFormatter
    {
        public override void Serialize(Stream s, object o)
        {
            RawString rs = (RawString)o;
            byte[] ba = rs.ToByteArray();
            s.Write(ba, 0, ba.Length);
        }

        public override object Deserialize(Stream stm)
        {
            StreamReader sr = new StreamReader(stm, true);
            string s = sr.ReadToEnd();
            return new RawString(s);
        }
    }

    [CustomFormatter(typeof(RawStringFormatter))]
    [Serializable]
    public class RawString
    {
        [XmlIgnore]
        string _val;

        public RawString(string s)
        {
            if (null == s)
                throw new ArgumentNullException();
            _val = s;
        }

        public RawString()
        {
        }

        public byte[] ToByteArray()
        {
            return Encoding.UTF8.GetBytes(_val);
        }

        public override string ToString()
        {
            return _val;
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM