生成 HTML table 并在 Apache NiFI 中通过电子邮件发送

Generate HTML table and send it vie email in Apache NiFI

我们有一个 Java 应用程序,它从数据库中读取信息并通过 Freemarker 生成 HTML table,如下所示:

<#if marks?size != 0>
<div>
    <p>
        <b>Total rows with information about broken utm-marks for ${date} is: ${total}. Displayed in current report: ${displayed}</b>
    </p>
    <br/>
</div>
<table border="1" cellspacing="0" cellpadding="1">
    <tr class="tableHeader" style = "background-color:#f8f5e4; text-align:center; font-weight: bold;">
        <th>Report date</th>
        <th>Account Login</th>
        <th>View Id</th>
        <th>Utm marks</th>
        <th>Exception type</th>
        <th>Exception message</th>
    </tr>
    <#list marks as mark>
        <tr class="tableBody">
            <td>${(mark.reportDate)!""}</td>
            <td>${(mark.accountLogin)!""}</td>
            <td>${(mark.accountViewId)!""}</td>
            <td>${(mark.utmMarks)!""}</td>
            <td>${(mark.exceptionType)!""}</td>
            <td>${(mark.exceptionMessage)!""}</td>
        </tr>
    </#list>
</table>
<br/>
<#else>
<div>
    <p>
        <b>No information about broken utm marks for ${date}.</b>
    </p>
</div>
</#if>

此生成的 table 将发送到配置的电子邮件。

是否可以使用 Apache NiFi(不使用和使用 ExecuteScript)构建此类应用程序?从数据库读取 - 很好;发送电子邮件 - 很好;但是模板和 html table 呢?

在 nifi 根文件夹中创建 ./templates 文件夹

将模板文件 test.ftlh 放在那里,内容为:

<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome ${user}!</h1>
  <p>Our latest product:
  <a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>

使用 GenerateFlowFile 将以下 json 注入流文件:

{
  "user":"Big Joe",
  "latestProduct": {
    "name":"green mouse",
    "url":"aaa/bbb/ccc"
  }
}

使用ExecuteGroovyScript合并模板与数据

@Grab(group='org.freemarker', module='freemarker', version='2.3.31')
import freemarker.template.*
import groovy.json.*

class Const{
    static Configuration cfg
}
//on processor start
static onStart(ProcessContext context){
    Const.cfg = new Configuration(Configuration.VERSION_2_3_29)
    Const.cfg.with{
        setDirectoryForTemplateLoading(new File("./templates"))
        setDefaultEncoding("UTF-8")
        setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER)
        setLogTemplateExceptions(false)
        setWrapUncheckedExceptions(true)
        setFallbackOnNullLoopVariable(false)
    }
}
//flowfile process
def ff=session.get()
if(!ff)return

ff.write{InputStream rawIn, OutputStream rawOut->
    //assume json in flowfile
    def root = new JsonSlurper().parse(rawIn)
    Template tpl = Const.cfg.getTemplate("test.ftlh")
    rawOut.withWriter("UTF-8"){w-> tpl.process(root, w) }
}

REL_SUCCESS << ff

没有 ExecuteScript(或类似的)的一种方法是:

  • 将 HTML 模板文本放入 NiFi 进程组变量中
  • 然后使用NiFi表达式语言函数evaluateELString将流文件属性代入模板。

这种方法的优点是易于维护:HTML 模板文本可以与流程组一起使用;没有脚本代码需要维护。

为了说明这种方法,假设您将 HTML 模板文本放入名为 email_template 的进程组变量中。此外,假设流文件具有 HTML 模板所需的属性集(例如,来自数据库)。然后为了获得实现的电子邮件正文,将 NiFi 表达式 ${email_template:evaluateELString()} 放入 Replacement 属性 的 ReplaceText[=40] 的值中=] 处理器(在 PutEmail 处理器之前)。

在上图中,变量email_template可以设置为:

<#if marks?size != 0>
<div>
    <p>
        <b>Total rows with information about broken utm-marks for ${date} is: ${total}. Displayed in current report: ${displayed}</b>
    </p>
    <br/>
</div>
<table border="1" cellspacing="0" cellpadding="1">
    <tr class="tableHeader" style = "background-color:#f8f5e4; text-align:center; font-weight: bold;">
        <th>Report date</th>
        <th>Account Login</th>
        <th>View Id</th>
        <th>Utm marks</th>
        <th>Exception type</th>
        <th>Exception message</th>
    </tr>
    <#list marks as mark>
        <tr class="tableBody">
            <td>${mark.reportDate}</td>
            <td>${mark.accountLogin}</td>
            <td>${mark.accountViewId}</td>
            <td>${mark.utmMarks}</td>
            <td>${mark.exceptionType}</td>
            <td>${mark.exceptionMessage}</td>
        </tr>
    </#list>
</table>
<br/>
<#else>
<div>
    <p>
        <b>No information about broken utm marks for ${date}.</b>
    </p>
</div>
</#if>

...和 ​​${email_template:evaluateELString()} 将用流文件属性值替换 datetotaldisplayedmark.reportDate