信创国产化背景下,哪些富文本编辑器支持Word公式的本地化粘贴?
·
农业集团信息管理系统富文本增强功能开发全记录
一、需求分析与技术选型
作为湖南某农业集团技术负责人,近期接到客户核心需求:在现有信息管理系统的后台文章发布模块中新增两大功能:
- 智能粘贴功能:支持从Word/微信公众号直接粘贴内容,图片自动上传至专用存储服务器(未来可平滑迁移至多云对象存储)
- 文档导入功能:支持Word/Excel/PPT/PDF全格式导入,完整保留样式与图片
技术栈现状:
- 前端:Vue2-CLI + UEditor(百度开源版)
- 后端:SpringBoot 2.7.x + Oracle 19c
- 部署环境:内网私有化部署(需支持离线环境)
- 存储方案:初期采用文件系统存储,预留对象存储接口
二、技术调研与方案评估
1. 富文本编辑器扩展方案
UEditor深度定制:
- 发现官方已停止维护,但社区活跃度较高
- 核心需求需解决:
- 保留Word样式(需解析Office Open XML)
- 图片二进制流处理(避免Base64污染DOM)
- 微信公众号特殊格式兼容
替代方案对比:
| 方案 | 优势 | 风险 |
|---|---|---|
| 扩展UEditor | 现有系统兼容性好,迁移成本低 | 需自行处理复杂文档解析 |
| 迁移wangEditor | 现代架构,支持插件机制 | 需重构现有编辑器集成 |
| 引入TinyMCE | 企业版支持Office文档 | 商业授权成本高 |
| 泽优WordPaster | 完全开源(下载源码) | |
| 支持任何系统 | ||
| 支持任何开发语言 | ||
| 支持主流编辑器 | ||
| 免费技术支持(QQ群:223813913) | 需要终端安全控件 |
最终决策:基于UEditor进行二次开发,通过插件机制实现功能扩展
2. 文档解析技术选型
Word/PPT解析:
- 采用Apache POI升级版(poi-tl 3.12+)
- 关键特性:
// 示例:提取Word中的图片与样式 XWPFDocument doc = new XWPFDocument(inputStream); doc.getParagraphs().forEach(para -> { // 提取段落样式 CTR ctr = para.getCTR(); // 提取嵌入图片 doc.getAllPictures().forEach(pic -> { byte[] imageData = pic.getData(); // 处理二进制流... }); });
PDF解析:
- 选用PDFBox 2.0.27(Apache许可)
- 实现要点:
- 文本流提取时保留位置信息
- 图片提取需处理CCITT压缩格式
Excel解析:
- 使用EasyExcel 3.3.2(阿里开源)
- 特殊处理:
- 合并单元格样式映射
- 图表对象提取(暂不支持)
3. 存储架构设计
初期方案:
/storage
/images
/doc
{docId}/
{md5}.jpg
/temp
对象存储接口:
public interface CloudStorageService {
String upload(byte[] data, String ext, String module);
void delete(String key);
// 实现类包括AliyunOSSImpl, HuaweiOBSImpl等
}
三、核心功能实现
1. 智能粘贴功能开发
前端实现:
// UEditor插件扩展
UE.plugins['smartpaste'] = function() {
const me = this;
// 监听粘贴事件
me.addListener('beforepaste', function(type, html) {
// 微信公众号内容特殊处理
if (isWechatContent(html)) {
return processWechatContent(html);
}
// Word内容处理
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const images = doc.querySelectorAll('img[src^="blob:"]');
// 批量上传图片
return Promise.all(
Array.from(images).map(img => {
return fetch(img.src).then(res => res.blob())
.then(blob => uploadImage(blob));
})
).then(urls => {
// 替换图片URL
let i = 0;
return html.replace(/]+src="blob:[^"]+"/g, () => {
return ` handleImage(
@RequestParam("file") MultipartFile file,
@RequestParam(value = "module", defaultValue = "doc") String module) {
// 生成唯一文件名
String filename = UUID.randomUUID() +
StringUtils.getExtension(file.getOriginalFilename());
// 存储二进制数据
String path = storageService.store(
file.getBytes(),
filename,
module
);
return ResponseEntity.ok(new UploadResult(path));
}
}
2. 文档导入功能实现
统一处理入口:
@Service
public class DocumentImportService {
public ImportResult importDocument(MultipartFile file) throws IOException {
String ext = StringUtils.getExtension(file.getOriginalFilename());
switch (ext.toLowerCase()) {
case "docx":
case "doc":
return new WordImporter().importDoc(file);
case "xlsx":
case "xls":
return new ExcelImporter().importXls(file);
case "pptx":
case "ppt":
return new PptImporter().importPpt(file);
case "pdf":
return new PdfImporter().importPdf(file);
default:
throw new IllegalArgumentException("不支持的文档类型");
}
}
}
Word导入核心逻辑:
public class WordImporter {
public ImportResult importDoc(MultipartFile file) throws IOException {
try (XWPFDocument doc = new XWPFDocument(file.getInputStream())) {
ImportResult result = new ImportResult();
// 处理段落
doc.getParagraphs().forEach(para -> {
String text = para.getText();
if (StringUtils.isNotBlank(text)) {
result.addContent(text);
}
// 处理段落样式
CTR ctr = para.getCTR();
if (ctr.isSetRPr()) {
CTRPr rpr = ctr.getRPr();
// 解析字体、颜色等样式...
}
});
// 处理图片(异步上传)
List pictures = doc.getAllPictures();
pictures.forEach(pic -> {
String url = uploadService.upload(
pic.getData(),
pic.suggestFileExtension(),
"doc"
);
result.addImage(url);
});
return result;
}
}
}
四、关键问题解决
1. 样式保留方案
字体处理:
- 建立字体映射表(解决Windows特有字体问题)
private static final Map FONT_MAP = Map.of(
"Calibri", "Arial",
"Microsoft YaHei", "SimHei"
);
颜色转换:
// 前端样式转换
function convertColor(officeColor) {
// 处理Word中的RGB颜色(如#FF0000)
if (/^#[0-9A-F]{6}$/i.test(officeColor)) {
return officeColor;
}
// 处理RGB值(如rgb(255,0,0))
const match = officeColor.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
if (match) {
return `#${match.slice(1).map(c =>
parseInt(c).toString(16).padStart(2, '0')
).join('')}`;
}
return '#000000';
}
2. 性能优化措施
图片处理:
- 后端启用异步上传队列(RabbitMQ实现)
- 前端实现图片懒加载
大文件处理:
- 分片上传(基于WebUploader组件)
- 导入进度显示(WebSocket实时推送)
3. 安全加固方案
文件校验:
public class FileValidator {
private static final Set ALLOWED_TYPES = Set.of(
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf"
);
public static void validate(MultipartFile file) {
// 文件类型校验
String ext = StringUtils.getExtension(file.getOriginalFilename());
if (!ALLOWED_TYPES.contains(ext.toLowerCase())) {
throw new SecurityException("非法文件类型");
}
// 文件内容校验(魔数检测)
byte[] header = new byte[8];
try (InputStream is = file.getInputStream()) {
is.read(header);
// 检测PDF魔数(%PDF-)
if (ext.equals("pdf") && !new String(header).startsWith("%PDF-")) {
throw new SecurityException("无效的PDF文件");
}
// 其他格式检测...
} catch (IOException e) {
throw new RuntimeException("文件读取失败", e);
}
}
}
五、部署与测试
1. 私有化部署方案
Docker化部署:
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE=target/doc-manager-*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
Oracle配置:
# application.properties
spring.datasource.url=jdbc:oracle:thin:@//192.168.1.100:1521/ORCL
spring.datasource.username=DOC_MANAGER
spring.datasource.password=ENC(加密密码)
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
2. 测试用例设计
核心场景测试:
| 测试类型 | 测试用例 | 预期结果 |
|---|---|---|
| 粘贴功能 | 从Word复制带图片的内容粘贴 | 图片正确上传,样式保留 |
| 导入功能 | 导入复杂格式的PPT | 幻灯片布局正确转换 |
| 性能测试 | 导入50MB的PDF文件 | 5分钟内完成,内存不溢出 |
| 安全测试 | 上传恶意脚本文件 | 系统拒绝并记录日志 |
六、后续优化方向
- 多云存储适配:开发阿里云/华为云等对象存储适配器
- 移动端适配:优化H5端的粘贴体验
- AI增强:集成OCR识别实现图片文字提取
- 版本控制:增加文档历史版本管理功能
整个开发周期历时6周,通过深度定制UEditor和精心设计后端处理流程,成功实现了客户要求的复杂文档处理功能。系统在内网环境稳定运行3个月后,已开始规划向公有云环境的迁移方案。
复制插件目录

引入插件文件
UEditor 1.4.3.3示例
注意:不要重复引入jquery,如果您的项目已经引入了jq,则不用再引入jq-1.4
在工具栏中增加插件按钮
//工具栏上的所有的功能按钮和下拉框,可以在new编辑器的实例时选择自己需要的重新定义
toolbars: [
[
"fullscreen",
"source",
"|",
"zycapture",
"|",
"wordpaster","importwordtoimg","netpaster","wordimport","excelimport","pptimport","pdfimport",
"|",
"importword","exportword","importpdf"
]
]
初始化控件

var pos = window.location.href.lastIndexOf("/");
var api = [
window.location.href.substr(0, pos + 1),
"asp/upload.asp"
].join("");
WordPaster.getInstance({
//上传接口:http://www.ncmem.com/doc/view.aspx?id=d88b60a2b0204af1ba62fa66288203ed
PostUrl: api,
//为图片地址增加域名:http://www.ncmem.com/doc/view.aspx?id=704cd302ebd346b486adf39cf4553936
ImageUrl: "",
//设置文件字段名称:http://www.ncmem.com/doc/view.aspx?id=c3ad06c2ae31454cb418ceb2b8da7c45
FileFieldName: "file",
//提取图片地址:http://www.ncmem.com/doc/view.aspx?id=07e3f323d22d4571ad213441ab8530d1
ImageMatch: ''
});//加载控件
注意
如果接口字段名称不是file,请配置FileFieldName。ueditor接口中使用的upfile字段
点击查看详细教程
配置ImageMatch
匹配图片地址,如果服务器返回的是JSON则需要通过正则匹配
ImageMatch: '',
配置ImageUrl
为图片地址增加域名,如果服务器返回的图片地址是相对路径,可通过此属性添加自定义域名。
ImageUrl: "",
配置SESSION
如果接口有权限验证(登陆验证,SESSION验证),请配置COOKIE。或取消权限验证。
参考:http://www.ncmem.com/doc/view.aspx?id=8602DDBF62374D189725BF17367125F3
效果
编辑器界面

导入Word文档,支持doc,docx

导入Excel文档,支持xls,xlsx

粘贴Word
一键粘贴Word内容,自动上传Word中的图片,保留文字样式。
Word转图片
一键导入Word文件,并将Word文件转换成图片上传到服务器中。
导入PDF
一键导入PDF文件,并将PDF转换成图片上传到服务器中。
导入PPT
一键导入PPT文件,并将PPT转换成图片上传到服务器中。
上传网络图片

下载示例
更多推荐



所有评论(0)