May 30, 2016, 11:33 AM
1 Posts

Adding a file as MIMEPART

  • Category: Server Side JavaScript
  • Platform: Windows
  • Release: 9.0.1
  • Role: Developer
  • Tags:
  • Replies: 0

Hi All,

I'm working on a XPages Documentation Wiki copy,
and I'm trying to to convert all the BASE64 images that are pasted to the rich-text editor to attached-image files so they won't reduce performances
(for now, base64-images copy is disabled via cliend-side js).

THIS IS HOW I DO IT:
In the xPage "all properties" tab, under "data" & the relevant document, there is a script for "postSaveDocument" event.
There I'm calling a SSJS function called "handleBase64" & send to it as params - the rich-text field name & the xspDocument (where the attachments should go).
This function (code below) is:
1. Getting the HTML code from the relevant entity
2. Making the MIME Entity "multipart" if its not.
3. Iterating over the HTML code - for every occurrence of an HTML IMAGE TAG which is a base64 -
the BASE64 code itself is being sent to a JAVA library (code also below) that CREATES A CHILD ENTITY for the MIME PART, and save the image file in it.
4. Changing the old HTML CODE - replacing the BASE64 image tags' src attribute with a "cid:[AttachmentName]".
5. SAVE.

THE PROBLEM IS AT STAGE 2 !
When adding base64 images to a document which it's rich-text field is not MULTIPART MIME yet,
as you can see, I'm trying to change it to a MULTIPART MIME ENTITY,
because otherwise the "CreateChildEntity" method in the JAVA library will crash.
When I'm "turning" the MIME ENTITY from TEXT to MULTIPART - everything looks well on the first edit\save -
The HTML referring to the attachments instead of the base64 data, and those are saved as attachments in the rich-text field.
BUT when I'm editing & saving again with new base64 images - they won't be shown.

I pretty sure that there is something wrong with my code that should turn the MIME to MULTIPART, so i marked it with BOLD RED.

SSJS CODE THAT RUNS ON "postSaveDocument" EVENT:
function handleBase64 (richTextFieldName, xspDocument) {
    var targetDoc = xspDocument.getDocument(true);
    //GETTING THE HTML CODE FROM THE RELEVANT ENTITY
    var htmlEntityFound = false;
    var allEnteties = targetDoc.getMIMEEntity(richTextFieldName);
    if (allEnteties.getContentType() == "text") {
        var htmlChildEntity = currChildEntity;
        var html = allEnteties.getContentAsText();
        htmlEntityFound = true;
    } else if (allEnteties.getContentType() == "multipart") {
        var currChildEntity:NotesMIMEEntity = allEnteties.getFirstChildEntity();
        while (currChildEntity && currChildEntity.getContentType() != "text") {
            currChildEntity = currChildEntity.getNextEntity();
        }
        if (currChildEntity) {
            var html = currChildEntity.getContentAsText();
            htmlEntityFound = true;
        }
    }
    //CHECK IF THERE ARE BASE64 IMAGES. IF NOT - DONE!
    if (htmlEntityFound && html.match(/<img(.)+?src="data:image\/(...|....);base64,(.)+?>/i) != null) {
        //FIND & CONVERT BASE64 IMAGES!
        session.setConvertMIME(false);
        var htmlWithoutBase64Tags = html.split(/<img(.)+?src="data:image\/(...|....);base64,(.)+?>/i);
        var htmlConvertedImages = [];
        var htmlConvertedImagesExtraAttributes = [];
        htmlNextImageTag = html.match(/<img(.)+?src="data:image\/(...|....);base64,(.)+?>/i);
        if (htmlNextImageTag != null) {
            htmlNextImageTag = htmlNextImageTag[0];
        }
        //CHECK IF THERE IS A MULTIPART ENTITY. IF NOT - CREATE ONE!
        if (targetDoc.getMIMEEntity(richTextFieldName).getContentType() != 'multipart') {
            var ent2 = targetDoc.getMIMEEntity(richTextFieldName);
            var parent = ent2.createParentEntity();
            var child = parent.createChildEntity();
        }

        var j = 0;
        //ITERATE THROUGH BASE64 IMAGES TAGS AND TAKE CARE OF THEM
        while (htmlNextImageTag) {
            var base64str = htmlNextImageTag.substr(htmlNextImageTag.search(/src="data:image\/(...|....);base64,/i) + 27, htmlNextImageTag.length);
            if (base64str.charAt(0) == ",") base64str = base64str.substr(1, base64str.length);
            base64str = base64str.substr(0, base64str.indexOf('"'));
            var fileExtension = htmlNextImageTag.substr(htmlNextImageTag.search(/src="data:image\//i) + 16, htmlNextImageTag.length);
            fileExtension = fileExtension.substr(0, fileExtension.search(/;base64,/i));
            var extraAttributes = htmlNextImageTag.split("<img").join(" ").split(/src="data:image\/(...|....);base64,(.)+?"/i).join(" ").split(/(\/>|>)/).join(" ");
            if (extraAttributes == null || extraAttributes == "") {
                extraAttributes = ' ';
            }
            //FIND NUMBER OF BASE64 CONVERTED IMAGE
            var filename = "attachedconvertedbase64image";
            var emptyFileNameFound = false;
            while (!emptyFileNameFound && j < 5000) {
                emptyFileNameFound = true;
                var allEnteties = targetDoc.getMIMEEntity(richTextFieldName);
                if (allEnteties != null) {
                    var currChildEntity:NotesMIMEEntity = allEnteties.getFirstChildEntity();
                    while (currChildEntity && emptyFileNameFound) {
                        if (currChildEntity.getHeaders().indexOf("attachedconvertedbase64image" + j) != -1) {
                            emptyFileNameFound = false;
                        }
                        currChildEntity = currChildEntity.getNextEntity();
                    }
                }
                if (emptyFileNameFound) {
                    filename = "attachedconvertedbase64image" + j;
                }
                j++;
            }
            //ACTUAL SAVE - BY EXTERNAL JAVA LIBRARY
            var saveImage:com.dalet.xpages.SaveBase64Image = new com.dalet.xpages.SaveBase64Image(targetDoc, base64str, filename, fileExtension, richTextFieldName);
            saveImage.process();

            //REMOVE FROM LONG OLD-HTML STRING, SAVE NAME FOR LATER AND FIND NEXT BASE64-IMAGE-TAG
            html = html.replace(/<img(.)+?src="data:image\/(...|....);base64,(.)+?>/i, '');
            htmlConvertedImages.push(filename);
            htmlConvertedImagesExtraAttributes.push(extraAttributes);
            htmlNextImageTag = html.match(/<img(.)+?src="data:image\/(...|....);base64,(.)+?>/i);
            if (htmlNextImageTag != null) {
                htmlNextImageTag = htmlNextImageTag[0];
            }
        }
        //CREATE THE NEW HTML STRING & SAVE IT
        var newHtml = '';
        for (var k = 0; k < htmlWithoutBase64Tags.length - 1; k++) {
            newHtml += htmlWithoutBase64Tags[k] + '<img ' + htmlConvertedImagesExtraAttributes[k] + ' src=cid:' + htmlConvertedImages[k] + ' />';
        }
        newHtml += htmlWithoutBase64Tags[htmlWithoutBase64Tags.length - 1];
        //FIND THE HTML-TEXT ENTITY & REPLACE IT WITH NEW HTML
        var allEnteties = targetDoc.getMIMEEntity(richTextFieldName);
        var currChildEntity:NotesMIMEEntity = allEnteties.getFirstChildEntity();
        while (currChildEntity && currChildEntity.getHeaders().indexOf('text/html') == -1) {
            currChildEntity = currChildEntity.getNextEntity();
        }
        if (currChildEntity) {
            var stream = session.createStream();
            stream.writeText(newHtml);
            currChildEntity.setContentFromText(stream ,"text/html;charset=UTF-8", 1725);
            stream.close();
            targetDoc.closeMIMEEntities(true, 'content');
            targetDoc.save(true);
        }
        session.setConvertMIME(true);
    }
    return true;
}

JAVA BASE64 CONVERSION LIBRARY:
import java.io.ByteArrayInputStream;
import javax.xml.bind.DatatypeConverter;
import lotus.domino.Document;
import lotus.domino.MIMEEntity;
import lotus.domino.MIMEHeader;
import lotus.domino.Session;
import lotus.domino.Stream;
import com.ibm.xsp.model.domino.DominoUtils;

public class SaveBase64Image {
    Document targetDoc;
    String b64String;
    String currFileName;
    String richTextFieldName;
    String fileExtension;

    public SaveBase64Image(Document targetDoc, String imageBase64Str, String filename, String fileExtension, String richTextFieldName) {
        this.targetDoc = targetDoc;
        this.b64String = imageBase64Str;
        this.currFileName = filename;
        this.richTextFieldName = richTextFieldName;
        this.fileExtension = fileExtension;
    }

    public void process() {
        if (b64String.trim().equals("")) {
            System.out.println("Error in SaveBase64Image.process() - b64str Empty");
        }
        
        try {
            // Convert the Base64 string to binary
            byte[] imgBytes = DatatypeConverter.parseBase64Binary(b64String);
            
            Session s = DominoUtils.getCurrentSession();
            s.setConvertMime(false);

            // Create MIME to store the image
            MIMEEntity richtext = targetDoc.getMIMEEntity(this.richTextFieldName);
            MIMEEntity child = richtext.createChildEntity();
            MIMEHeader header = child.createHeader("Content-Disposition");
            header.setHeaderVal("inline; filename=\"" + this.currFileName + "." + this.fileExtension + "\"" + ";");
            MIMEHeader header2 = child.createHeader("Content-ID");
            header2.setHeaderVal("<" + this.currFileName + ">");
            
            // Add the image to MIME via stream
            Stream stream = s.createStream();
            stream.setContents(new ByteArrayInputStream(imgBytes));
            child.setContentFromBytes(stream, "image/" + this.fileExtension, MIMEEntity.ENC_IDENTITY_BINARY);
            stream.close();
            
            s.setConvertMime(true);
        } catch (Exception e) {
            System.out.println("Error in SaveBase64Image.process()");
            e.printStackTrace();
        }
    }
}