在日常 Excel 数据处理中,排序是最常用的操作之一——无论是按名称、数值还是日期重新排列数据,都能让表格更有条理、便于检索。Spire.XLS for JavaScript 基于 WebAssembly 在浏览器端直接完成数据排序,通过虚拟文件系统(VFS)管理输入输出文件,无需后端服务支持。
本文介绍两个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.XLS for JavaScript。以下示例默认已安装 Spire.XLS 并完成 WebAssembly 模块初始化。
对指定单元格区域进行升序排序是最常见的数据整理需求。Spire.XLS for JavaScript 通过 Workbook.DataSorter.SortColumns.Add() 方法添加排序字段并指定排序方式,再通过 Workbook.DataSorter.Sort() 方法对指定区域进行排序。具体操作步骤如下:
Workbook 对象,并使用 LoadFromFile() 方法加载 Excel 文档。Workbook.Worksheets.get() 方法获取指定工作表。Workbook.DataSorter.SortColumns.Add() 方法添加排序字段,指定排序列和排序方式。Workbook.DataSorter.Sort() 方法对指定单元格区域进行排序。Workbook.SaveToFile() 方法保存文档到指定路径。下面是一个完整的代码示例,展示了在 React 中按单个列对单元格区域进行升序排序:
function App() {
const sortAscending = async () => {
// 获取 Spire.XLS WASM 模块
const xlsModule = window.wasmModule?.spirexls;
// 检查模块是否就绪
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// 将字体和 Excel 文件载入 VFS
await window.spire.FetchFileToVFS('simsun.ttc', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'DataSorting.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// 加载工作簿
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
// 获取第一个工作表
const sheet = workbook.Worksheets.get(0);
// 添加排序字段:按第 5 列(人口)升序排序
workbook.DataSorter.SortColumns.Add({ key: 4, orderBy: xlsModule.OrderBy.Ascending });
// 对指定单元格区域 A1:E19 排序
workbook.DataSorter.Sort(sheet.Range.get("A1:E19"));
// 保存文档
const outputFileName = 'SortDataAscending_output.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
// 释放资源
workbook.Dispose();
// 从 VFS 读取转换后的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Sort Data in Ascending Order</h1>
<button onClick={sortAscending}>
Start
</button>
</div>
);
}
export default App;
排序后,数据将按照第 5 列(人口)的数值从小到大升序排列,同一行中的其余列数据会与人口列保持对齐。

当单列排序无法满足需求时,可以同时按多个列进行排序。Spire.XLS for JavaScript 支持多次调用 SortColumns.Add() 方法添加多个排序字段,排序时先按第一个字段排序,再按后续字段排序。具体操作步骤如下:
Workbook 对象,并使用 LoadFromFile() 方法加载 Excel 文档。Workbook.Worksheets.get() 方法获取指定工作表。Workbook.DataSorter.SortColumns.Add() 方法添加多个排序字段。Workbook.DataSorter.Sort() 方法对指定单元格区域进行排序。Workbook.SaveToFile() 方法保存文档到指定路径。下面是一个完整的代码示例,展示了在 React 中按多个列对单元格区域进行排序:
function App() {
const sortMultipleColumns = async () => {
// 获取 Spire.XLS WASM 模块
const xlsModule = window.wasmModule?.spirexls;
// 检查模块是否就绪
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// 将字体和 Excel 文件载入 VFS
await window.spire.FetchFileToVFS('simsun.ttc', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'DataSorting.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// 加载工作簿
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
// 获取第一个工作表
const sheet = workbook.Worksheets.get(0);
// 添加多个排序字段:先按第 3 列(洲)升序,再按第 4 列(面积)升序
workbook.DataSorter.SortColumns.Add({ key: 2, orderBy: xlsModule.OrderBy.Ascending });
workbook.DataSorter.SortColumns.Add({ key: 3, orderBy: xlsModule.OrderBy.Ascending });
// 对指定单元格区域 A1:E19 排序
workbook.DataSorter.Sort(sheet.Range.get("A1:E19"));
// 保存文档
const outputFileName = 'SortDataMultipleColumns_output.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
// 释放资源
workbook.Dispose();
// 从 VFS 读取转换后的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Sort Data by Multiple Columns</h1>
<button onClick={sortMultipleColumns}>
Start
</button>
</div>
);
}
export default App;
排序后,数据先按第 3 列(洲)升序排列,将同属一个洲的国家归拢在一起;当洲相同时,再按第 4 列(面积)升序排列。

原因:DataSorter.Sort() 方法默认将排序区域的首行视为标题行并保留在原位置。如果表头被移到了数据行中,通常是排序区域起始行设置不正确。
解决:确认传入 Sort() 方法的区域包含表头行,且表头行位于区域的顶部,例如 sheet.Range.get("A1:E19")。也可以从数据行开始排序,如 sheet.Range.get("A2:E19")。
原因:排序只对传入 Sort() 方法的单元格区域生效。如果只对单独一列的区域排序,其他列不会跟随重排,导致同行数据错位。
解决:让排序区域覆盖所有相关列(例如包含名称、首都、洲、面积、人口的完整区域 A1:E19),从而保证整行数据一起移动。
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。
在 Web 应用中处理 Excel 文件时,查找和替换数据是常见的需求。Spire.XLS for JavaScript 基于 WebAssembly 在浏览器端直接完成 Excel 操作,通过虚拟文件系统(VFS)管理输入输出文件,无需后端服务支持。它提供了 FindAllString() 和 FindAllNumber() 等查找方法,支持在整个工作表或指定的单元格区域中查找目标数据,并快速将其替换为新的内容,同时还可以通过高亮颜色标记替换后的单元格。
通过 Spire.XLS for JavaScript,您既可以批量替换整个工作表中的文本,也可以将查找范围限定在特定的单元格区域内,在需要精确更新部分数据时更加灵活高效。
本文介绍两个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.XLS for JavaScript。以下示例默认已安装 Spire.XLS 并完成 WebAssembly 模块初始化。
使用 Spire.XLS for JavaScript,您可以在整个工作表中查找包含指定文本的单元格,并将其替换为新的内容。FindAllString() 方法会返回所有匹配的单元格区域,您可以通过设置 range.Text 属性来替换文本,并通过 range.Style.Color 属性为替换后的单元格设置高亮颜色,从而清晰地标记被修改的位置。具体操作步骤如下:
Workbook 对象并加载已有的 Excel 文件。workbook.Worksheets.get() 获取要操作的工作表。worksheet.FindAllString() 查找工作表中包含指定文本的所有单元格区域。range.Text 替换文本,并通过 range.Style.Color 设置高亮颜色。SaveToFile() 保存工作簿为 Excel 文件。下面是一个完整的代码示例,展示了在 React 中查找并替换整个工作表中的数据:
function App() {
const findAndReplace = async () => {
// 获取 Spire.XLS WASM 模块
const xlsModule = window.wasmModule?.spirexls;
// 检查模块是否就绪
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
let excelFileName = 'Sample.xlsx';
await window.spire.FetchFileToVFS(excelFileName, '', `${process.env.PUBLIC_URL}static/data/`);
// 创建新工作簿并加载已有的 Excel 文件
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: excelFileName });
// 获取第一个工作表
let worksheet = workbook.Worksheets.get(0);
// 查找工作表中包含 "总计" 文本的所有单元格
let ranges = worksheet.FindAllString("总计", false, false);
// 遍历查找结果,替换文本并设置高亮颜色
for (let range of ranges) {
range.Text = "总支出";
range.Style.Color = xlsModule.Color.get_Yellow();
}
// 保存工作簿
const outputFileName = 'FindAndReplaceData.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
workbook.Dispose();
// 从 VFS 读取文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>在 Excel 中查找并替换工作表数据</h1>
<button onClick={findAndReplace}>
生成
</button>
</div>
);
}
export default App;
在 Excel 中查找并替换工作表数据

当只需要更新部分数据时,您可以将查找范围限定在特定的单元格区域内。使用 sheet.Range.get() 方法指定目标区域后,range.FindAllString() 会在该区域内查找包含指定文本的单元格,从而确保区域外的数据不受影响。具体操作步骤如下:
Workbook 对象并加载已有的 Excel 文件。workbook.Worksheets.get() 获取要操作的工作表。sheet.Range.get() 指定要查找的单元格区域。range.FindAllString() 在指定区域内查找包含目标文本的单元格,遍历结果并替换文本、设置高亮颜色。SaveToFile() 保存工作簿为 Excel 文件。下面是一个完整的代码示例,展示了在 React 中查找并替换特定单元格区域中的数据:
function App() {
const findAndReplaceInRange = async () => {
// 获取 Spire.XLS WASM 模块
const xlsModule = window.wasmModule?.spirexls;
// 检查模块是否就绪
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// 示例文件加载到虚拟文件系统(VFS)
let excelFileName = 'FindCellsSample.xlsx';
await window.spire.FetchFileToVFS(excelFileName, '', `${process.env.PUBLIC_URL}static/data/`);
// 创建新工作簿并加载已有的 Excel 文件
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: excelFileName });
// 获取第一个工作表
let worksheet = workbook.Worksheets.get(0);
// 指定要查找的单元格区域
let range = worksheet.Range.get({
row: 1,
column: 1,
lastRow: 12,
lastColumn: 2,
});
// 在指定区域内查找包含 "总计" 文本的单元格
let ranges = range.FindAllString("总计", false, false);
// 遍历查找结果,替换文本并设置高亮颜色
for (let r of ranges) {
r.Text = "总支出";
r.Style.Color = xlsModule.Color.get_Yellow();
}
// 保存工作簿
const outputFileName = 'FindAndReplaceInRange.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
workbook.Dispose();
// 从 VFS 读取文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>在指定区域查找并替换数据</h1>
<button onClick={findAndReplaceInRange}>
生成
</button>
</div>
);
}
export default App;
在 Excel 中查找并替换特定单元格区域中的数据

原因:FindAllString() 方法的后两个布尔参数分别用于控制查找是否区分大小写以及是否要求匹配整个单词。如果参数设置不当,可能会找到过多或过少的匹配结果。
解决:根据实际需求调整 FindAllString() 的参数:
// 不区分大小写,不要求整个单词匹配
let ranges = worksheet.FindAllString("Area", false, false);
// 区分大小写,要求整个单词匹配
let ranges = worksheet.FindAllString("Total", true, true);
原因:查找和替换不仅适用于文本,也适用于数字。如果只使用 FindAllString() 处理文本,将无法匹配数值型的单元格。
解决:使用 range.FindAllNumber() 方法在指定区域内查找数字,然后通过设置 Text 属性替换数值:
let numberRanges = range.FindAllNumber(100, true);
for (let r of numberRanges) {
r.Text = "200";
r.Style.Color = xlsModule.Color.get_Yellow();
}
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。

快速概览: 本文介绍如何在 React 应用中使用 JavaScript 和 Spire.Doc WebAssembly(WASM),直接在浏览器中将 Markdown 文件或字符串转换为 HTML,无需服务器端处理。
Markdown 常用于 README 文件、技术文档、技术文章以及其他结构化内容。但在某些应用场景中,需要将这些内容转换为 HTML 文件,例如发布为网页,或交给其他基于 HTML 的工作流继续处理。
本文将介绍如何在 React 应用中使用 Spire.Doc for JavaScript 将 Markdown 转换为 HTML,主要包括以下两种常见场景:
在 React 项目的根目录中打开终端,并通过 NPM 安装 Spire.Doc:
npm i spire.office
安装完成后,将 node_modules/spire.office 中的以下运行时资源复制到 React 项目的 public 目录:
下面的示例还会使用 CALIBRI.ttf 进行文本渲染。请将该字体文件放置在 public/static/font/ 目录下。
对于基于文件的转换示例,请将源 Markdown 文档放置在 public/static/data/MarkdownExample.md。
有关详细的项目配置方法,请参阅 如何在 React 中集成 Spire.Doc for JavaScript。
注意: 以下示例使用
process.env.PUBLIC_URL,这是 Create React App 中常用的公共资源路径写法。如果项目使用 Vite 或其他构建工具,请根据实际项目结构调整公共资源路径。
如果 Markdown 内容已经以 .md 文件形式存在,可以先将文件加载到 WebAssembly 虚拟文件系统(VFS)中,再通过 Document.LoadFromFile() 打开。随后,使用 Document.SaveToFile() 即可将文档导出为 HTML。
基于文件的转换主要分为以下四个阶段:
FetchFileToVFS() 将所需字体和源 Markdown 文件加载到 VFS。FileFormat.Markdown 加载 .md 文件,并通过 FileFormat.Html 保存为 HTML。下面的示例将 MarkdownExample.md 转换为 MarkdownToHtml.html。
import React, { useState, useEffect } from 'react';
function App() {
const [wasmModule, setWasmModule] = useState(null);
// 加载 Spire.Doc
useEffect(() => {
(async () => {
try {
const publicUrl = process.env.PUBLIC_URL || '';
const spireModule = await import(
/* webpackIgnore: true */
`${publicUrl}/spire.doc.js`
);
const rawModule = spireModule.default || spireModule;
window.wasmModule =
typeof rawModule === 'function'
? await rawModule({
locateFile: (path) =>
path.endsWith('.wasm')
? `${publicUrl}/${path}`
: path
})
: rawModule;
setWasmModule(window.wasmModule);
} catch (error) {
console.error(
'Failed to load spire.doc.js WASM module:',
error
);
}
})();
}, []);
// 将 Markdown 文件转换为 HTML
const convertMarkdownFileToHtml = async () => {
const wasmModule = window.wasmModule?.spiredoc;
if (!wasmModule) return;
// 将所需字体加载到 VFS
await window.spire.FetchFileToVFS(
'CALIBRI.ttf',
'/Library/Fonts/',
`${process.env.PUBLIC_URL}/static/font/`
);
// 将 Markdown 文件加载到 VFS
const inputFileName = 'MarkdownExample.md';
await window.spire.FetchFileToVFS(
inputFileName,
'',
`${process.env.PUBLIC_URL}/static/data/`
);
// 创建 Document 实例
const doc = new wasmModule.Document();
try {
// 加载 Markdown 文档
doc.LoadFromFile({
fileName: inputFileName,
fileFormat: wasmModule.FileFormat.Markdown
});
// 设置 HTML 导出选项
doc.HtmlExportOptions.CssStyleSheetType = wasmModule.CssStyleSheetType.Internal;
doc.HtmlExportOptions.ImageEmbedded = true;
// 将文档保存为 HTML
const outputFileName = 'MarkdownToHtml.html';
doc.SaveToFile({
fileName: outputFileName,
fileFormat: wasmModule.FileFormat.Html
});
// 从 VFS 中读取生成的 HTML 文件
const htmlBytes =
window.dotnetRuntime.Module.FS.readFile(
outputFileName
);
// 下载 HTML 文件
const blob = new Blob(
[htmlBytes],
{ type: 'text/html;charset=utf-8' }
);
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = outputFileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
} finally {
doc.Dispose();
}
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>转换 Markdown 文件为 HTML</h1>
<button
onClick={convertMarkdownFileToHtml}
disabled={!wasmModule}
>
转换并下载
</button>
</div>
);
}
export default App;
WebAssembly 模块加载完成后,点击 转换并下载。应用会从 public/static/data/ 目录加载 MarkdownExample.md,将其转换为 HTML,并下载生成的 MarkdownToHtml.html 文件。
在这一过程中,FetchFileToVFS() 用于将源 Markdown 文件加载到 WebAssembly 虚拟文件系统中,Document.LoadFromFile() 用于从 VFS 读取该文件。CssStyleSheetType.Internal 和 ImageEmbedded 用于将样式和图片直接嵌入 HTML 中,最后通过 Document.SaveToFile() 将文档导出为 HTML。
输出结果:

Markdown 内容也经常直接在应用中生成或编辑。例如,从 API 或 CMS 返回的内容可能已经是 JavaScript 字符串,而不是现有的 .md 文件。
由于 Document.LoadFromFile() 需要从 WebAssembly 虚拟文件系统中读取文件,因此可以先使用 FS.writeFile() 将 Markdown 字符串写入一个临时 .md 文件,再按照处理普通 Markdown 文件的方式完成转换。
基于字符串的转换主要包括以下五个步骤:
下面的示例将一段包含标题、列表、代码、链接和表格的 Markdown 字符串转换为 HTML。
import React, { useState, useEffect } from 'react';
function App() {
const [wasmModule, setWasmModule] = useState(null);
// 加载 Spire.Doc
useEffect(() => {
(async () => {
try {
const publicUrl = process.env.PUBLIC_URL || '';
const spireModule = await import(
/* webpackIgnore: true */
`${publicUrl}/spire.doc.js`
);
const rawModule = spireModule.default || spireModule;
window.wasmModule =
typeof rawModule === 'function'
? await rawModule({
locateFile: (path) =>
path.endsWith('.wasm')
? `${publicUrl}/${path}`
: path
})
: rawModule;
setWasmModule(window.wasmModule);
} catch (error) {
console.error(
'Failed to load spire.doc.js WASM module:',
error
);
}
})();
}, []);
// 将 Markdown 字符串转换为 HTML
const convertMarkdownStringToHtml = async () => {
const wasmModule = window.wasmModule?.spiredoc;
if (!wasmModule) return;
// 将所需字体加载到 VFS
await window.spire.FetchFileToVFS(
'CALIBRI.ttf',
'/Library/Fonts/',
`${process.env.PUBLIC_URL}/static/font/`
);
// 定义 Markdown 字符串
const markdownString = `# 项目文档
本项目提供一个**基于浏览器的文档转换工具**。
## 功能特点
- 将 Markdown 转换为 HTML
- 在浏览器中直接处理文档内容
- 导出生成的 HTML 文件
## 代码示例
\`\`\`javascript
function greet(name) {
console.log(\`你好,\${name}!\`);
}
greet("世界");
\`\`\`
## 支持的内容
| 功能 | 是否支持 |
|------|----------|
| 标题 | 是 |
| 列表 | 是 |
| 表格 | 是 |
| 链接 | 是 |
有关 Markdown 的更多信息,请访问 [Markdown 官方指南](https://www.markdownguide.org/)。
`;
const inputFileName = 'MarkdownString.md';
const outputFileName = 'MarkdownStringToHtml.html';
// 将 Markdown 字符串写入 VFS
window.dotnetRuntime.Module.FS.writeFile(
inputFileName,
markdownString,
{ encoding: 'utf8' }
);
// 创建 Document 实例
const doc = new wasmModule.Document();
try {
// 加载 Markdown 文档
doc.LoadFromFile({
fileName: inputFileName,
fileFormat: wasmModule.FileFormat.Markdown
});
// 设置 HTML 导出选项
doc.HtmlExportOptions.CssStyleSheetType = wasmModule.CssStyleSheetType.Internal;
doc.HtmlExportOptions.ImageEmbedded = true;
// 将文档保存为 HTML
doc.SaveToFile({
fileName: outputFileName,
fileFormat: wasmModule.FileFormat.Html
});
// 从 VFS 中读取生成的 HTML 文件
const htmlBytes =
window.dotnetRuntime.Module.FS.readFile(
outputFileName
);
// 下载 HTML 文件
const blob = new Blob(
[htmlBytes],
{ type: 'text/html;charset=utf-8' }
);
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = outputFileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
} finally {
doc.Dispose();
}
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>转换 Markdown 字符串为 HTML</h1>
<button
onClick={convertMarkdownStringToHtml}
disabled={!wasmModule}
>
转换并下载
</button>
</div>
);
}
export default App;
与前一个示例不同,这里没有现成的 .md 文件需要加载,而是直接通过 FS.writeFile() 将 Markdown 内容写入 VFS:
window.dotnetRuntime.Module.FS.writeFile(
inputFileName,
markdownString,
{ encoding: 'utf8' }
);
这种方式同样适用于从 API、数据库、CMS 或文本编辑器中获取的 Markdown 内容。实际使用时,无需在代码中直接定义 markdownString,只需将获取到的 Markdown 内容传入 FS.writeFile() 即可。
输出结果:

在 React JavaScript 项目中,常见的转换问题通常与 WebAssembly 初始化、公共资源路径配置,或文件未正确加载到 VFS 有关。下表列出了几种常见问题以及优先检查的内容。
| 问题 | 可能原因 | 检查方法 |
|---|---|---|
spiredoc 为 undefined |
WASM 尚未初始化完成就开始执行转换 | 在 wasmModule 可用之前保持转换按钮禁用状态 |
加载运行时文件时出现 404 |
一个或多个 Spire.Doc 资源缺失,或公共资源路径配置不正确 | 检查 spire.doc.js、_framework/、WASM 资源以及浏览器的 Network 面板 |
无法加载 MarkdownExample.md |
传递给 FetchFileToVFS() 的源文件路径不正确 |
确认文件位于 public/static/data/ 目录下 |
| 字体加载失败 | 缺少 CALIBRI.ttf,或字体路径配置不正确 |
确认字体文件可以通过 public/static/font/ 路径访问 |
| 本地转换正常,但部署后失败 | 部署环境使用了不同的公共基础路径 | 检查生成的资源 URL,并调整 process.env.PUBLIC_URL 或对应构建工具中的公共路径设置 |
| 多次转换后浏览器内存持续增加 | 创建的 Document 对象未释放 |
每次转换完成后调用 doc.Dispose(),建议放在 finally 块中执行 |
A:通过 <input type="file"> 选择的文件,与存放在应用公共资源目录中的 Markdown 文件处理方式不同。
可以先使用浏览器 File API 读取用户选择的文件:
const markdownString = await file.text();
然后使用 FS.writeFile() 将字符串写入 VFS,并按照前面的 Markdown 字符串转换流程进行处理。
A:可以。先从 VFS 中读取生成的 HTML,并对返回的字节数据进行解码:
const htmlBytes = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const html = new TextDecoder('utf-8').decode(htmlBytes);
得到 HTML 字符串后,可以通过 iframe 显示:
<iframe
title="HTML Preview"
srcDoc={html}
/>
安全提示: 如果 Markdown 内容来自不受信任的用户或外部来源,应同样将生成的 HTML 视为不受信任内容。在生产环境中渲染之前,应对其进行安全过滤或采用隔离方式展示。
A:不需要。在上述示例中,文档处理通过 WebAssembly 直接在浏览器中完成,源 Markdown 和生成的 HTML 均通过客户端虚拟文件系统进行处理。
不过,如果应用需要存储生成的文件、读取受保护的源内容,或执行其他服务器端操作,仍然可能需要后端服务配合。
本文介绍了如何在 React 中使用 JavaScript 将 Markdown 转换为 HTML,包括 Markdown 文件和 Markdown 字符串两种常见输入方式。借助 WebAssembly,可以直接在浏览器中处理来自文件、编辑器、API 或 CMS 的 Markdown 内容,并将其转换为可下载、可预览或可继续处理的 HTML。两种场景使用的是同一套核心转换逻辑,因此也便于在不同 Markdown 数据来源之间复用。
Spire.PDF for Java 12.9.0 现已正式发布。该版本新增了对 PdfGridCell 富文本的支持,以及 PdfToWordConverter 流输入接口的支持,并修复了 PDF 转图片、PDF 转 PDF/A-2B、 高版本 JDK 模块系统兼容性以及 PDF 合并等方面的问题。详情如下。
新功能:
// 添加富文本
cellContent1.setRichText("<html><body><p style='color:black; font-size:14pt;'>Hello World</p></body></html>");
InputStream fileStream = new FileInputStream(inputFile);
PdfToWordConverter convert= new PdfToWordConverter(fileStream);
convert.saveToDocx(OutputFile);
问题修复:
透视表(PivotTable)是 Excel 中用于快速汇总、分析大量数据的核心工具,通过拖拽字段即可实现数据的统计与对比。Spire.XLS for JavaScript 基于 WebAssembly 在浏览器端直接完成透视表的创建、筛选与更新操作,通过虚拟文件系统(VFS)管理输入输出文件,无需后端服务支持。
本文介绍三个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.XLS for JavaScript。以下示例默认已安装 Spire.XLS 并完成 WebAssembly 模块初始化。
创建透视表通常包括四个步骤:准备源数据、添加透视表、设置字段布局以及计算数据。下面示例在第一个工作表中写入一份产品销售记录,然后通过 PivotCaches.Add 方法基于数据区域创建缓存,使用 PivotTables.Add 方法在工作表中添加透视表,最后将字段拖拽到行区域和数据区域完成布局。
function App() {
const createPivotTable = async () => {
// 获取 Spire.XLS WASM 模块
const xlsModule = window.wasmModule?.spirexls;
// 检查模块是否就绪
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// 将字体载入 VFS
await window.spire.FetchFileToVFS('simsun.ttc', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// 创建新工作簿
const workbook = new xlsModule.Workbook();
// 获取第一个工作表
const sheet = workbook.Worksheets.get(0);
// 向单元格写入源数据
sheet.Range.get('A1').Value = 'Product';
sheet.Range.get('B1').Value = 'Month';
sheet.Range.get('C1').Value = 'Count';
sheet.Range.get('A2').Value = 'SpireDoc';
sheet.Range.get('A3').Value = 'SpireDoc';
sheet.Range.get('A4').Value = 'SpireXls';
sheet.Range.get('A5').Value = 'SpireDoc';
sheet.Range.get('A6').Value = 'SpireXls';
sheet.Range.get('A7').Value = 'SpireXls';
sheet.Range.get('B2').Value = 'January';
sheet.Range.get('B3').Value = 'February';
sheet.Range.get('B4').Value = 'January';
sheet.Range.get('B5').Value = 'January';
sheet.Range.get('B6').Value = 'February';
sheet.Range.get('B7').Value = 'February';
sheet.Range.get('C2').Value = '10';
sheet.Range.get('C3').Value = '15';
sheet.Range.get('C4').Value = '9';
sheet.Range.get('C5').Value = '7';
sheet.Range.get('C6').Value = '8';
sheet.Range.get('C7').Value = '10';
// 基于数据区域创建透视表缓存
const dataRange = sheet.Range.get('A1:C7');
const cache = workbook.PivotCaches.Add({ range: dataRange });
// 添加透视表
const pt = sheet.PivotTables.Add('Pivot Table', sheet.Range.get({ row: 10, column: 5 }), cache);
// 将字段拖入行区域
const pf1 = pt.PivotFields.get_Item('Product');
pf1.Axis = xlsModule.AxisTypes.Row;
const pf2 = pt.PivotFields.get_Item('Month');
pf2.Axis = xlsModule.AxisTypes.Row;
// 将字段拖入数据区域
pt.DataFields.Add(pt.PivotFields.get_Item('Count'), 'Sum of Count', xlsModule.SubtotalTypes.Sum);
// 设置透视表样式
pt.BuiltInStyle = xlsModule.PivotBuiltInStyles.PivotStyleMedium12;
// 计算透视表数据
pt.CalculateData();
sheet.AutoFitColumn(5);
sheet.AutoFitColumn(6);
// 保存工作簿
const outputFileName = 'CreatePivotTable_output.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
// 释放资源
workbook.Dispose();
// 从 VFS 读取生成的文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Create Pivot Table</h1>
<button onClick={createPivotTable}>Start</button>
</div>
);
}
export default App;
通过 CalculateData 方法计算后,透视表会按产品与月份汇总各产品的数量总和,并以设置的 PivotStyleMedium12 样式显示。

当透视表包含较多数据时,可以通过在行字段上添加筛选,只保留符合条件的数据行。
function App() {
const filterPivotTable = async () => {
// 获取 Spire.XLS WASM 模块
const xlsModule = window.wasmModule?.spirexls;
// 检查模块是否就绪
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// 将字体和 Excel 文件载入 VFS
await window.spire.FetchFileToVFS('simsun.ttc', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'PivotTableExample.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// 加载工作簿
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName, version: xlsModule.ExcelVersion.Version2010 });
// 获取第二个工作表(PivotTable)中的第一个透视表
const pt = workbook.Worksheets.get(1).PivotTables.get(0);
// 获取透视表的第一个行字段
const rowField = pt.RowFields.get(0);
// 为行字段添加数值筛选:第一个数据字段的值小于 5300000
rowField.AddValueFilter(xlsModule.PivotValueFilterType.LessThan, pt.DataFields.get(0), window.spire.Double.Create(5300000), new window.spire.SpireObject(0));
// 重新计算透视表数据
pt.CalculateData();
// 保存工作簿
const outputFileName = 'FilterPivotTable_output.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
// 释放资源
workbook.Dispose();
// 从 VFS 读取生成的文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Filter Pivot Table</h1>
<button onClick={filterPivotTable}>Start</button>
</div>
);
}
export default App;
原透视表数据
筛选完成后,透视表的行区域只保留满足筛选条件的数据,便于聚焦分析特定范围的数据。

当透视表的基础数据发生变化时,需要更新数据源并刷新透视表缓存,才能让透视表反映最新的汇总结果。
function App() {
const updateDataSource = async () => {
// 获取 Spire.XLS WASM 模块
const xlsModule = window.wasmModule?.spirexls;
// 检查模块是否就绪
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// 将字体和 Excel 文件载入 VFS
await window.spire.FetchFileToVFS('simsun.ttc', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'PivotTableExample.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// 加载工作簿
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName, version: xlsModule.ExcelVersion.Version2010 });
// 获取数据源工作表并修改其中的单元格值
const data = workbook.Worksheets.get('Data');
data.Range.get('A2').Text = 'NewValue';
data.Range.get('D2').NumberValue = 28000;
// 获取透视表所在的工作表
const sheet = workbook.Worksheets.get({ sheetName: 'PivotTable' });
// 获取工作表上的第一个透视表
const pt = sheet.PivotTables.get(0);
// 刷新透视表缓存
pt.Cache.IsRefreshOnLoad = true;
// 计算并更新透视表数据
pt.CalculateData();
// 保存工作簿
const outputFileName = 'UpdateDataSource_output.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
// 释放资源
workbook.Dispose();
// 从 VFS 读取生成的文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Update Pivot Table Data Source</h1>
<button onClick={updateDataSource}>Start</button>
</div>
);
}
export default App;
数据源更新并刷新后,透视表中对应的汇总结果会同步更新。

原因:透视表添加字段后未调用 CalculateData 方法,或数据字段未正确添加到数据区域。
解决:完成字段布局后调用 pt.CalculateData() 重新计算透视表,并确保通过 DataFields.Add 方法将数值字段添加到数据区域。
原因:添加标签或数值筛选后未调用 CalculateData 方法重新计算,或筛选添加到了错误的字段上。
解决:调用 pt.CalculateData() 重新计算透视表,并确认使用 pt.RowFields.get(0) 等属性获取正确的字段后再添加筛选。
原因:修改数据源后未刷新透视表缓存,导致透视表仍保留旧的数据。
解决:修改数据源后,将 pt.Cache.IsRefreshOnLoad 设置为 true 并调用 pt.CalculateData(),使透视表基于最新的数据源重新计算。
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。
Spire.Office 11.8.0 已正式发布。该版本新增了一些功能,如Spire.XLS 新增了对 TEXTSPLIT 公式的支持;Spire.Barcode 同步了 BarcodeSettings 枚举设置到 NETStandard 平台。除此之外,一些在操作Word、Excel、PDF和PPT文档时出现的问题也已成功被修复。更多新功能及问题修复详情如下。
该版本涵盖了最新版的Spire.Doc、Spire.PDF、Spire.XLS、Spire.Presentation、Spire.DataExport、Spire.Barcode、Spire.DocViewer、Spire.PDFViewer、Spire.OfficeViewer、Spire.Email。
https://www.e-iceblue.cn/Downloads/Spire-Office-NET.html
问题修复:
新功能:
sheet.getCellRange("A8").setFormula("=TEXTSPLIT(\"A,B,C\", \",\")");
问题修复:
问题修复:
调整:
| 原类名 | 新类名 |
| PdfTextMarkupAnnotationWidget | PdfTextMarkupAnnotation |
| PdfFreeTextAnnotationWidget | PdfFreeTextAnnotation |
| PdfTextAnnotationWidget | PdfTextAnnotation |
| PdfPopupAnnotationWidget | PdfPopupAnnotation |
| PdfLineAnnotationWidget | PdfLineAnnotation |
| PdfRubberStampAnnotationWidget | PdfRubberStampAnnotation |
| PdfPolyLineAnnotation | PdfPolylineAnnotation |
| PdfAttachmentAnnotation | PdfFileAttachmentAnnotation |
| PdfEmbeddedGoToAction | PdfGoToEmbeddedAction |
| PdfTextMarkupAnnotation | PdfHighlightAnnotation |
| 原属性 | 新属性 | 说明 |
| xxx.Text | xxx.Contents | 注释文本内容 |
| xxx.Author | xxx.Title | 注释作者 / 标题 |
| xxx.Bounds | xxx.Rectangle | 注释区域矩形 |
| xxx.TextMarkupColor | xxx.Color | 高亮 / 标记颜色 |
| xxx.Opacity | xxx.StrokingOpacity | 描边不透明度 |
| xxx.LineEndingStyle | xxx.CalloutLineEndingStyle | 引出线端点样式 |
| xxx.CalloutLines | xxx.CalloutLine | 引出线点数组 |
| xxx.BorderStyle / xxx.BorderWidth(lineBorder) | xxx.Border.Style / xxx.Border.Width | 线条边框样式 / 宽度 |
| xxx.BeginLineStyle / xxx.EndLineStyle | xxx.StartingStyle / xxx.EndingStyle | 线条端点样式 |
| xxx.InnerLineColor / xxx.BackColor | xxx.InteriorColor / xxx.Color | 线条内部颜色 / 背景色 |
| xxx.LeaderLineExt | xxx.LeaderLineExtension | 引线延长 |
| xxx.BorderEffect = PdfBorderEffect.BigCloud | xxx.Border.Effect = PdfBorderEffect.Cloudy | 边框云状效果 |
问题修复:
新功能:
setting.BorderDashStyle = Spire.Barcode.Publics.Drawing.DashStyle.DashDotDot;
Spire.Barcode.Publics.Drawing.StringAlignment.Center;
问题修复:
Spire.Office for Java 11.8.0 已上线。在该版本中,Spire.Doc for Java 增强了 Word 到 PDF 和 SVG 的转换功能;Spire.XLS for Java 修复了 Vaadin 25 + Java 21 环境下项目打包问题;Spire.PDF for Python 优化 PDF 转 Word 转换功能;Spire.Presentation for Java 增强了 PPT 到图片的转换效果。此外,本次更新还成功修复了多个已知问题。更多详情如下。
https://www.e-iceblue.cn/Downloads/Spire-Office-JAVA.html
问题修复:
问题修复:
问题修复:
问题修复:
Spire.XLS for Java 16.8.4 现已发布。该版本修复了在 Vaadin 25 + Java 21 环境下打包项目时,因内部类继承 final 类导致构建失败的问题。详情如下。
问题修复:
我们很高兴地宣布 12.8.20 版本正式发布。本次更新重点修复 PDF、OFD 转 Word 文档过程中出现的各类缺陷,程序运行稳定性与版式还原精准度均得到提升。详细更新内容如下:
问题修复:
Spire.Presentation for Java 11.8.3 已发布。该版本主要修复了一个在将 PPT 转换为图片时出现的问题。详情请查看以下内容。
问题修复:
https://www.e-iceblue.cn/Downloads/Spire-Presentation-JAVA.html