JavaScript 富文本转纯文本并下载 TXT 文件

RichTextEditor

提取编辑器当前文字,下载 UTF-8 TXT,包含安全预览、Unicode 检查与旧文件失效处理。

明确选择纯文本还是 HTML

getPlainText() 返回编辑器提取的文字,getHTMLCode() 返回标记。用正则表达式删标签可能拼接单词或留下未解码实体。纯文本导出会丢失格式,不只是删除尖括号。它适合阅读副本、索引输入或支持记录,不是富文本文档备份。

运行完整下载示例

通过应用提供编辑器资源。准备按钮读取当前文档,将 CRLF 和 CR 统一为 LF,创建不额外添加 BOM 的 UTF-8 text/plain Blob。独立链接下载 document.txt。预览使用 textContent,因此类似 HTML 的文字不会被渲染。编辑后旧链接失效,重新准备才能下载最新内容。示例不发送服务器请求。

<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css">
<script src="/richtexteditor/rte.js"></script>
<script src="/richtexteditor/plugins/all_plugins.js"></script>
<style>body{font:16px/1.6 system-ui;max-width:960px;margin:24px auto;padding:0 16px}button,a{box-sizing:border-box;display:inline-flex;align-items:center;min-height:44px;padding:8px 16px}a[hidden]{display:none}pre{white-space:pre-wrap;overflow-wrap:anywhere}.editor-scroll{max-width:100%;overflow-x:auto}</style>
<h1>Export rich text as a UTF-8 text file</h1>
<div class="editor-scroll"><div id="editor"></div></div>
<button type="button" id="prepare">Prepare text file</button>
<a id="download" download="document.txt" hidden>Download .txt</a>
<p id="status" role="status" aria-live="polite"></p>
<pre id="preview"></pre>
<script>
const editor = new RichTextEditor("#editor", { toolbar: "full", height: "320px" });
editor.setHTMLCode("<h2>Project notes</h2><p>Keep the words, including <strong>formatting</strong>, 中文 and 👋.</p>");
const link = document.getElementById("download");
const preview = document.getElementById("preview");
const status = document.getElementById("status");
let currentURL = null, preparedHTML = null;
function clearPreparedFile() {
  if (currentURL) {
    const obsoleteURL = currentURL;
    // Do not revoke synchronously: a download may still be starting.
    setTimeout(() => URL.revokeObjectURL(obsoleteURL), 30000);
    currentURL = null;
  }
  preparedHTML = null;
  link.removeAttribute("href");
  link.hidden = true;
  preview.textContent = "";
}
function prepare() {
  clearPreparedFile();
  // Export the API result, not the HTML source or a regex-stripped version.
  const text = editor.getPlainText().replace(/\r\n?/g, "\n");
  preparedHTML = editor.getHTMLCode();
  const file = new Blob([text], { type: "text/plain;charset=utf-8" });
  currentURL = URL.createObjectURL(file);
  link.href = currentURL;
  link.hidden = false;
  preview.textContent = text;
  status.textContent = "Text file ready: " + file.size + " UTF-8 bytes. Click Download .txt. Nothing sent to a server.";
}
document.getElementById("prepare").addEventListener("click", prepare);
editor.attachEvent("change", () => {
  // Delayed notifications for the same saved HTML do not invalidate this snapshot.
  if (preparedHTML !== null && editor.getHTMLCode() === preparedHTML) return;
  clearPreparedFile();
  status.textContent = "Content changed. Prepare a new file before downloading.";
});
window.textDemo = { editor, prepare };
</script>

了解纯文本会丢失什么

段落边界、列表标记、表格分隔符、图片替代文字及链接取决于编辑器的文本提取规则。应测试真实文档,不要承诺结果等同于 Markdown 或 Word。TXT 不保留图片、样式、评论、修订历史或可供往返编辑的 HTML。需要恢复草稿时另存原始 HTML。 空编辑器也可能提取出换行。本示例保留提取后的空白,不进行 trim;焦点变化可能影响末尾空行。

管理文件生命周期与下载

对象 URL 会占用 Blob 内存。示例在旧 URL 失效后等待 30 秒再释放,不在点击链接的瞬间释放。页面卸载时浏览器会释放剩余 URL。这只是示例策略,不是通用的下载完成信号。测试重复下载、大文档和目标设备。download 属性只建议文件名,实际保存方式和位置由浏览器设置决定。 连续下载可能需要浏览器的多文件下载许可。不要绕过浏览器策略,也不要在用户无意下载时批量触发。

验收清单

导出表情、组合重音、中文、日文、多段落和表格。下载字节应与规范化的 getPlainText() 结果比较,不与 HTML 比较。测试空文档及看起来像标签的文字。准备后再编辑,旧链接必须消失。TXT 不是 CSV 转义、HTML 清理或服务器验证边界。

延伸阅读