How to Add and Remove MIME attachments correctly

落爺英雄遲暮 提交于 2019-12-25 02:26:45

问题


I am adding MIME attachments to a document like this

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)
}

and in a repeat I provide a delete button

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

the attachment are removed from the document, in Ytria I can see that the $FILE items are removed but not the BODY items. the problem with this is that if I add a new attachment to the same document all the attachments I removed previously come back

This is how the document looks before removing the attachments.

The file size here is unfortnuately 0Kb because I used the wrong screenshot. from the beginnin all $File items have correct size

This is how the document look after I removed the attachments (using the script above)

This is what the document look like after I add one attachment (using the script above) after I removed them

  • Could I be doing something wrong when adding or removing the attachments? (see script)
  • It does not seem to matter if the Body field has the "store content as MIME" option set or not
  • see also this question How to Add and Remove attachments using MIME

回答1:


If you work with MIME methods to attach the file why not work with MIME methods to remove it as well?

I use my own framework so the following code might give you the impression to overcomplicate things but hopefully you should get the gist of it:

I have an enum that helps me navigate through the various MIME types. In this case you are dealing with 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;
    }

}

Then some helper classes:

@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);
    }

}

Finally the method that would do the job:

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();
    }

}


来源:https://stackoverflow.com/questions/53431447/how-to-add-and-remove-mime-attachments-correctly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!