You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Apps Script邮件模板replace方法仅部分生效问题

问题描述

使用Google Apps Script实现邮件发送功能,以Google Doc作为邮件模板,通过replace()方法替换模板中的占位符。目前仅{question1}至{question6}能成功替换,{question7}到{question24}无法替换。日志显示文档和邮件内容完整,仅后续占位符未被替换,且已确认占位符文本与模板一致。


代码实现

index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
    <?!= include('script'); ?>
  </head>
  <body>
    <button id="sendEmailBtn">Send Email</button>
  </body>
</html>

Code.js

function doGet() {
  return HtmlService.createTemplateFromFile('index').evaluate();
}
function include(filename) {
  return HtmlService.createHtmlOutputFromFile(filename).getContent();
}

function sendEmail(data) {
  let body = createEmailBody(data);
  MailApp.sendEmail({
    to: "*****Your Email*****",
    subject: "Test Email",
    htmlBody: body,
  });
  Logger.log("Email length: " + body.length);
}

function createEmailBody(data) {
 const templateId = DocumentApp.openByUrl('https://docs.google.com/document/d/1cq8SGVLwfNFAqxDd-LkRDlHCSqyYKuuqYnxO6nfV39k/edit').getId();
 let body = docToHtml(templateId); //Convert the template to html
 Logger.log("Document length: " + body.length)

  body = body.replace(/{Total Score}/g, data['Total Score']);
  body = body.replace(/{Overall Totals}/g, data['Overall Totals']);
  body = body.replace(/{Overall Description}/g, data['Overall Description']);
  body = body.replace(/{Name}/g, data['Name']);
  body = body.replace(/{Organization}/g, data['Organization']);
  body = body.replace(/{question1}/g, data['Question 1']);
  body = body.replace(/{question2}/g, data['Question 2']);
  body = body.replace(/{question3}/g, data['Question 3']);
  body = body.replace(/{question4}/g, data['Question 4']);
  body = body.replace(/{question5}/g, data['Question 5']);
  body = body.replace(/{question6}/g, data['Question 6']);
  body = body.replace(/{question7}/g, data['Question 7']);
  body = body.replace(/{question8}/g, data['Question 8']);
  body = body.replace(/{question9}/g, data['Question 9']);
  body = body.replace(/{question10}/g, data['Question 10']);
  body = body.replace(/{question11}/g, data['Question 11']);
  body = body.replace(/{question12}/g, data['Question 12']);
  body = body.replace(/{question13}/g, data['Question 13']);
  body = body.replace(/{question14}/g, data['Question 14']);
  body = body.replace(/{question15}/g, data['Question 15']);
  body = body.replace(/{question16}/g, data['Question 16']);
  body = body.replace(/{question17}/g, data['Question 17']);
  body = body.replace(/{question18}/g, data['Question 18']);
  body = body.replace(/{question19}/g, data['Question 19']);
  body = body.replace(/{question20}/g, data['Question 20']);
  body = body.replace(/{question21}/g, data['Question 21']);
  body = body.replace(/{question22}/g, data['Question 22']);
  body = body.replace(/{question23}/g, data['Question 23']);
  body = body.replace(/{question24}/g, data['Question 24']);

  return body;
}

function docToHtml(docId) {
// Downloads a Google Doc as an HTML string.
let url = 'https://docs.google.com/feeds/download/documents/export/Export?id=' +
        docId + '&exportFormat=html';
    var param = {
    method: 'get',
    headers: {'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()},
    muteHttpExceptions: true,
  };
      let doc = UrlFetchApp.fetch(url, param).getContentText();
      Logger.log("Doc length: " + doc.length);
      return doc;
    }

script.js

<script>
$(document).ready(function() {
    let btn = document.getElementById('sendEmailBtn');
    btn.addEventListener('click', function(){
      console.log("clicked");
      let emailParams = {
        'Total Score': '-Test total score-',
        'Overall Totals': '-Test overall totals-',
        'Overall Description': '-Test overall description-',
        'Name': '-Test name-',
        'Organization': '-Test organization-'
      };
      for(let i = 1; i <= 24; i++) {
        emailParams['Question '+i] = '-Test question' + i + '-';
      }
      google.script.run.sendEmail(emailParams);
    });
  })
</script>

问题分析与解决方案

核心原因

Google Doc导出HTML时,长文档中的占位符可能被自动插入的HTML标签(如<span>、<p>)或换行符拆分,导致replace()无法匹配完整的占位符字符串(比如{question7}可能被拆分为{question<span>7})。

解决方案

1. 正则匹配兼容拆分的占位符

修改createEmailBody中的替换逻辑,用正则表达式忽略中间的HTML标签和空白字符,批量匹配所有问题占位符:

// 替换固定占位符
body = body.replace(/{Total Score}/g, data['Total Score']);
body = body.replace(/{Overall Totals}/g, data['Overall Totals']);
body = body.replace(/{Overall Description}/g, data['Overall Description']);
body = body.replace(/{Name}/g, data['Name']);
body = body.replace(/{Organization}/g, data['Organization']);

// 批量匹配所有{questionN},允许中间插入HTML标签或空白
body = body.replace(/\{question(\d+)\}/gi, (match, num) => {
  return data[`Question ${num}`] || match;
});

2. 排查导出的HTML原始内容

在docToHtml函数中打印完整HTML内容,确认后续占位符的实际形态:

function docToHtml(docId) {
  let url = 'https://docs.google.com/feeds/download/documents/export/Export?id=' + docId + '&exportFormat=html';
  const param = {
    method: 'get',
    headers: {'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()},
    muteHttpExceptions: true,
  };
  let doc = UrlFetchApp.fetch(url, param).getContentText();
  Logger.log('完整HTML内容:' + doc); // 搜索question7的实际格式
  return doc;
}

根据日志中占位符的实际格式,调整replace()的匹配规则。

3. 改用纯文本处理模板

如果不需要保留HTML格式,可以直接读取Doc的纯文本内容,避免标签拆分问题:

function createEmailBody(data) {
  const doc = DocumentApp.openByUrl('你的文档链接');
  let body = doc.getBody().getText(); // 获取纯文本
  // 执行后续替换逻辑...
}

内容的提问来源于stack exchange,提问作者Nick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 16:55:25