如何正确添加和删除 MIME 附件

Posted

技术标签:

【中文标题】如何正确添加和删除 MIME 附件【英文标题】:How to Add and Remove MIME attachments correctly 【发布时间】:2019-04-25 03:59:48 【问题描述】:

我正在向这样的文档添加 MIME 附件

try
    var d = database.getView("Main").getFirstDocument()
    var it = d.getFirstItem("Body")
    var att:NotesEmbeddedObject = it.getEmbeddedObject("mydoc.docx")
    var streamDOC:NotesStream = session.createStream()

    streamDOC.setContents(att.getInputStream())


    var newd;
    newd = database.getView("NewD").getFirstDocument()
    if(newd==null)
        newd = database.createDocument()
        newd.replaceItemValue("Form","Main")
        var me = newd.createMIMEEntity("Body")
    else
        var me = newd.getMIMEEntity("Body") 
    

    var filename = "test.pdf"
    var mc = me.createChildEntity();
    var he = mc.createHeader("Content-Disposition")
    he.setHeaderVal("attachment; filename=\"" + filename + "\"");
    he = mc.createHeader("Content-ID");
    he.setHeaderVal( "<" + filename + ">" );
    mc.setContentFromBytes(streamDOC, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", NotesMIMEEntity.ENC_IDENTITY_8BIT);
    newd.save()
    print("success")
catch(e)
    print("fail " + e)

我提供了一个删除按钮

var eo = nd.getDocument().getAttachment(att)
eo.remove()
nd.save()

附件已从文档中删除,在 Ytria 中,我可以看到 $FILE 项目已删除,但 BODY 项目未删除。这样做的问题是,如果我在同一个文档中添加一个新附件,我之前删除的所有附件都会回来

这是删除附件之前文档的外观。

不幸的是,这里的文件大小为 0Kb,因为我使用了错误的屏幕截图。从一开始所有的 $File 项目都有正确的大小

这是我删除附件后文档的外观(使用上面的脚本)

这是我删除附件后添加一个附件(使用上面的脚本)后文档的样子

添加或删除附件时我做错了什么吗? (看 脚本) Body 字段是否有“商店”似乎无关紧要 内容为 MIME”选项设置与否 另请参阅此问题 How to Add and Remove attachments using MIME

【问题讨论】:

【参考方案1】:

如果您使用 MIME 方法来附加文件,为什么不使用 MIME 方法来删​​除它呢?

我使用自己的框架,因此以下代码可能会让您觉得事情过于复杂,但希望您能明白其中的要点:

我有一个枚举,可以帮助我浏览各种 MIME 类型。在这种情况下,您正在处理ATTACHMENT

public enum MimeContentType 

    ATTACHMENT("attachment") 

        @Override
        public boolean matches(String[] headers) 
            int score = 0;

            for (String header : headers) 
                if (header.startsWith("Content-Disposition")) 
                    score++;
                

                if (header.contains("attachment")) 
                    score++;
                

                if (header.contains("filename")) 
                    score++;
                

                if (score == 3) 
                    return true;
                
            

            return false;
        

    ,
    TEXT("text"),
    TEXT_html("text/html"),
    TEXT_PLAIN("text/plain");

    private final String type;

    private MimeContentType(String type) 
        this.type = type;
    

    public boolean matches(String[] headers) 
        for (String header : headers) 
            if (header.startsWith("Content-Type") && header.contains(type)) 
                return true;
            
        

        return false;
    


然后是一些辅助类:

@FunctionalInterface
public interface ThrowableConsumer<T> extends Consumer<T> 

    @Override
    default void accept(final T t) 
        try 
            acceptOrThrow(t);
         catch (final Throwable e) 
            throw new RuntimeException(e);
        
    

    void acceptOrThrow(T t) throws Throwable;



@FunctionalInterface
public interface ThrowableFunction<T, R> extends Function<T, R> 

    @Override
    default R apply(T t) 
        try 
            return applyOrThrow(t);
         catch (final Throwable e) 
            throw new RuntimeException(e);
        
    

    R applyOrThrow(T t) throws Throwable;



@FunctionalInterface
public interface ThrowablePredicate<T> extends Predicate<T> 

    @Override
    default boolean test(T t) 
        try 
            return testOrThrow(t);
         catch (final Throwable e) 
            throw new RuntimeException(e);
        
    

    boolean testOrThrow(T t) throws Throwable;



@FunctionalInterface
public interface ThrowableSupplier<T> extends Supplier<T> 

    @Override
    default T get() 
        try 
            return getOrThrow();
         catch (final Throwable e) 
            throw new RuntimeException(e);
        
    

    T getOrThrow() throws Throwable;



public enum DominoUtil 
    ;

    private static final Vector<String> MIME_FILTERED_HEADERS = new Vector<>();

    static 
        MIME_FILTERED_HEADERS.add("Content-Type");
        MIME_FILTERED_HEADERS.add("Content-Disposition");
    

    public static List<MIMEEntity> getMimeEntitiesByContentType(MIMEEntity entity,
            MimeContentType contentType) throws NotesException 
        Objects.requireNonNull(entity, "Entity cannot be null");
        Objects.requireNonNull(contentType, "Content type cannot be null");

        List<MIMEEntity> subentities = new ArrayList<>();
        MIMEEntity nextEntity = null;

        try 
            nextEntity = entity.getNextEntity();

            while (nextEntity != null) 
                String[] entityFilteredHeaders = nextEntity
                        .getSomeHeaders(MIME_FILTERED_HEADERS, true)
                        .split("\\n");

                if (contentType.matches(entityFilteredHeaders)) 
                    subentities.add(nextEntity);
                

                nextEntity = nextEntity.getNextEntity();
            
         finally 
            DominoUtil.recycle(nextEntity);
        

        return subentities;
    

    public final static MIMEEntity getMimeEntity(Document doc, String itemName,
            boolean createOnFail) throws NotesException 
        if (itemName == null) 
            throw new NullPointerException("Invalid MIME entity item name");
        

        MIMEEntity mimeEntity = doc.getMIMEEntity(itemName);

        if (mimeEntity == null) 
            if (doc.hasItem(itemName)) 
                doc.removeItem(itemName);
            

            if (createOnFail) 
                mimeEntity = doc.createMIMEEntity(itemName);
            
        

        return mimeEntity;
    

    public static Optional<String> getMimeEntityAttachmentFilename(MIMEEntity entity) throws NotesException 
        Objects.requireNonNull(entity, "Entity cannot be null");

        return getMimeEntityHeaderValAndParams(
                entity, (ThrowablePredicate<MIMEHeader>) h -> h.getHeaderVal().equals("attachment"))
                        .map(s -> 
                            Matcher m = Pattern.compile("filename=['\"]?([^'\"\\s]+)").matcher(s);
                            m.find();
                            return m.group(1);
                        );
    

    public static Optional<String> getMimeEntityHeaderValAndParams(
            MIMEEntity entity, Predicate<MIMEHeader> matcher) throws NotesException 
        Objects.requireNonNull(entity, "Entity cannot be null");
        Objects.requireNonNull(matcher, "Matcher cannot be null");

        Vector<?> headers = entity.getHeaderObjects();

        try 
            return headers
                    .stream()
                    .map(MIMEHeader.class::cast)
                    .filter(matcher)
                    .map((ThrowableFunction<MIMEHeader, String>) MIMEHeader::getHeaderValAndParams)
                    .findFirst();
         finally 
            recycle(headers);
        
    

    public static void recycle(Base... bases) 
        for (Base base : bases) 
            if (base != null) 
                try 
                    base.recycle();
                 catch (Exception e) 
                    // Do nothing
                
            
        
    

    public static void recycle(Collection<? extends Object> objs) 
        objs.stream()
                .filter(o -> o instanceof Base)
                .map(o -> (Base) o)
                .forEach(DominoUtil::recycle);
    


最后是可以完成这项工作的方法:

public class Example 

    public static void yourEntryPoint() 
        try 
            // The last param is just a way to create an attachment from text
            // You have InputStream to pass along obviously
            addAttachment(doc, "Body", "fake1.txt", "this is fake text1");
            addAttachment(doc, "Body", "fake2.txt", "this is fake text2");
            addAttachment(doc, "Body", "fake3.txt", "this is fake text3");
            removeAttachment(doc, "Body", "fake2.txt");
            removeAttachment(doc, "Body", "fake3.txt");

         catch (NotesException e) 
            throw new RuntimeException(e);
        
    

    private static void addAttachment(Document doc, String itemName, String fileName, String data)
            throws NotesException 
        MIMEEntity mimeEntity = null;
        Stream stm = null;

        try 
            mimeEntity = DominoUtil.getMimeEntity(doc, itemName, true);

            Optional<MIMEEntity> optAttEntity = getAttachmentMimeEntity(mimeEntity, fileName);

            MIMEEntity attachmentEntity = null;

            if (optAttEntity.isPresent()) 
                attachmentEntity = optAttEntity.get();
             else 
                attachmentEntity = mimeEntity.createChildEntity();
                MIMEHeader header = attachmentEntity.createHeader("Content-Disposition");
                header.setHeaderValAndParams("attachment; filename=\"" + fileName + "\"");
            

            stm = doc.getParentDatabase().getParent().createStream();
            stm.writeText(data);

            attachmentEntity.setContentFromBytes(stm,
                    "application/octet-stream",
                    MIMEEntity.ENC_IDENTITY_BINARY);

            stm.close();

            doc.closeMIMEEntities(true, itemName);
         finally 
            DominoUtil.recycle(stm);
            DominoUtil.recycle(mimeEntity);
        
    

    private static void removeAttachment(Document doc, String itemName, String fileName)
            throws NotesException 
        MIMEEntity mimeEntity = null;

        try 
            // Get MIME entity
            mimeEntity = DominoUtil.getMimeEntity(doc, itemName, true);

            Optional<MIMEEntity> optAttEntity = getAttachmentMimeEntity(mimeEntity, fileName);

            if (!optAttEntity.isPresent()) 
                return;
            

            optAttEntity.get().remove();

            // Header cleaning on empty entity
            if (mimeEntity.getFirstChildEntity() != null) 
                doc.closeMIMEEntities(true, itemName);
             else 
                mimeEntity.remove();
            
         finally 
            DominoUtil.recycle(mimeEntity);
        
    

    private static Optional<MIMEEntity> getAttachmentMimeEntity(MIMEEntity root, String fileName)
            throws NotesException 
        return DominoUtil
                .getMimeEntitiesByContentType(root, MimeContentType.ATTACHMENT)
                .stream()
                .filter((ThrowablePredicate<MIMEEntity>) mime -> 
                    Optional<String> opt = DominoUtil.getMimeEntityAttachmentFilename(mime);

                    return opt.isPresent() && opt.get().equals(fileName);
                )
                .findFirst();
    


【讨论】:

谢谢,这可能会有所帮助,但您的代码不完整 public static Map> getMimeEntities(MIMEEntity entity, MimeContentType... contentTypes) 对不起,我不明白你的意思。我通过剥离所有其余代码来将代码放在一起,但剩下的对我来说似乎是完整的。我瞎了吗? 好的,我肯定忽略了导入语句。有哪些错误?你用的是 iava8 还是 6? 不,看看我在评论中显示的方法签名。我正在使用 DDE10 或者是三个点的某种语法

以上是关于如何正确添加和删除 MIME 附件的主要内容,如果未能解决你的问题,请参考以下文章

您将使用啥架构来存储 100 亿条 MIME 消息并使其可删除和全文搜索,包括。附件

如何编写多部分 MIME 混合消息以在 Outlook 中正确显示

python邮件脚本-添加zip

直接从 MIME 字符串将附件添加到 MailMessage

删除附件文件

如何删除图像作为附件但显示在电子邮件正文中