记一次在Vue2+SpringBoot项目里给UEditor加Word导入功能的奇妙冒险
第一章:需求突降,老板的夺命连环call
"小王啊,咱们后台发布文章那个编辑器得升级了!用户反馈说每次从Word复制内容到编辑器,图片全丢了,格式也乱成一锅粥,你得想办法解决!"电话那头传来老板急促的声音。
我盯着屏幕上那个熟悉的UEditor界面,心里咯噔一下——这不就是传说中的"Word内容搬家"难题吗?作为前端开发,我深知这个需求的坑有多深:Word生成的HTML代码比蜘蛛网还复杂,图片处理更是让人头大。但老板的命令就是圣旨,我立刻开启了我的技术探索之旅。
第二章:开源江湖寻宝记
第一站:CSDN博客的神秘线索
我在CSDN上疯狂搜索,突然被一篇标题为《UEDITOR富文本实现导入WORD功能》的文章吸引。文章里提到一个叫zyOffice的开源组件,号称能完美解决Word导入问题,还支持Vue2!
我火速下载了示例项目,发现这个组件的集成方式相当友好:
- 上传zyoffice文件夹到项目
- 在工具栏添加插件按钮
- 初始化组件时配置转换接口
// 在Vue组件中初始化zyOffice
zyOffice.getInstance({
word: "http://localhost:8080/api/word/convert",
wordExport: "http://localhost:8080/api/word/export",
ui: { render: "wdpst" }
});
最让我惊喜的是,这个组件居然支持信创国产化系统,比如中标麒麟、银河麒麟等,这对我们服务政府客户来说简直是福音!
第二站:GitHub的意外收获
在GitHub上闲逛时,我发现了另一个宝藏项目——WordPaster。这个项目专门解决富文本编辑器粘贴Word内容时的图片上传问题,而且完美支持UEditor!
项目文档里详细介绍了在Vue2中的集成步骤:
- 复制WordPaster插件目录到项目
- 引入必要的JS文件(注意不要重复引入jQuery)
- 在工具栏添加自定义按钮
- 初始化WordPaster组件
// 初始化WordPaster组件
const api = window.location.href.substr(0, window.location.href.lastIndexOf("/") + 1) + "api/upload";
WordPaster.getInstance({
PostUrl: api, // 上传接口地址
FileFilter: ".doc;.docx", // 支持的文件类型
MaxSize: 10 * 1024 * 1024 // 最大文件大小10MB
});
第三章:后端SpringBoot的攻坚战
有了前端组件还不够,后端处理才是重头戏。我参考了多个SpringBoot集成UEditor的方案,最终决定采用以下架构:
1. 文件转换服务
使用Apache POI处理Word文档,但发现直接解析复杂格式还是不够完美。于是转向使用Aspose.Words(虽然不是完全开源,但有免费试用版),它对Word格式的保留效果更好。
@RestController
@RequestMapping("/api/word")
public class WordController {
@PostMapping("/convert")
public ResponseEntity convertWordToHtml(@RequestParam("file") MultipartFile file) {
try {
// 使用Aspose.Words转换Word为HTML
Document doc = new Document(file.getInputStream());
HtmlSaveOptions options = new HtmlSaveOptions();
options.setExportImagesAsBase64(false); // 不使用Base64嵌入图片
options.setResourcesFolder("uploads/images/" + UUID.randomUUID()); // 图片保存路径
// 保存HTML和图片
String htmlPath = "uploads/html/" + UUID.randomUUID() + ".html";
doc.save(htmlPath, options);
// 返回处理后的HTML(需要提取图片URL)
String htmlContent = Files.readString(Paths.get(htmlPath));
// 这里需要进一步处理htmlContent,替换图片路径为可访问的URL
return ResponseEntity.ok(htmlContent);
} catch (Exception e) {
return ResponseEntity.badRequest().body("转换失败: " + e.getMessage());
}
}
}
2. 图片上传处理
配置UEditor的上传接口,使用SpringBoot的MultipartFile接收上传的文件:
@RestController
@RequestMapping("/ueditor")
public class UEditorController {
@Value("${file.upload-dir}")
private String uploadDir;
@PostMapping("/upload")
public Map uploadImage(@RequestParam("upfile") MultipartFile file) {
Map result = new HashMap<>();
try {
// 确保上传目录存在
File uploadPath = new File(uploadDir);
if (!uploadPath.exists()) {
uploadPath.mkdirs();
}
// 保存文件
String fileName = UUID.randomUUID().toString() +
file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
File dest = new File(uploadPath, fileName);
file.transferTo(dest);
// 返回UEditor需要的JSON格式
result.put("state", "SUCCESS");
result.put("url", "/uploads/" + fileName); // 前端可访问的URL
result.put("title", fileName);
result.put("original", file.getOriginalFilename());
} catch (IOException e) {
result.put("state", "ERROR");
result.put("message", "上传失败: " + e.getMessage());
}
return result;
}
}
第四章:数据库设计的智慧
关于富文本内容的存储,我参考了MySQL富文本存储方案对比,最终选择了以下方案:
- 内容表设计:
CREATE TABLE article (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(200) NOT NULL,
content TEXT NOT NULL, -- 存储HTML内容
content_html_path VARCHAR(500), -- 如果内容很大,可以存储HTML文件路径
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
- 图片资源表(可选):
CREATE TABLE article_image (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
article_id BIGINT NOT NULL,
image_url VARCHAR(500) NOT NULL,
alt_text VARCHAR(200),
sort_order INT DEFAULT 0,
FOREIGN KEY (article_id) REFERENCES article(id)
);
对于大多数情况,直接将HTML存储在content
字段就足够了。只有当内容特别大时,才考虑将HTML保存为文件,并在数据库中存储文件路径。
第五章:集成测试的酸甜苦辣
成功时刻
经过一周的奋战,终于迎来了第一次完整测试:
- 在Word中编写了一篇包含多种格式(标题、加粗、斜体、表格、图片)的文档
- 点击UEditor的"导入Word"按钮
- 见证奇迹的时刻——所有格式完美保留,图片自动上传并显示!
踩过的坑
- 图片路径问题:最初转换后的HTML中图片使用相对路径,导致前端无法显示。解决方案是在后端转换时统一替换为绝对URL。
- 跨域问题:前端和后端分离部署时,需要配置CORS。
- 样式冲突:Word生成的HTML带有大量内联样式,与UEditor的默认样式冲突。通过自定义CSS重置解决了这个问题。
第六章:最终胜利与经验总结
经过这次冒险,我总结出以下关键点:
- 选择合适的工具:zyOffice和WordPaster这两个组件大大简化了开发工作,避免了重复造轮子。
- 前后端协作:前端负责展示和交互,后端专注文件处理和存储,分工明确。
- 测试的重要性:不同版本的Word、不同浏览器都需要充分测试,确保兼容性。
- 性能优化:对于大文件,考虑分块上传和异步处理。
现在,我们的后台编辑器已经能够完美支持Word导入,用户反馈良好。这次经历也让我深刻认识到:在技术世界里,没有解决不了的问题,只有还没找到的解决方案!
最后,附上项目关键依赖版本信息:
- UEditor: 1.4.3.3
- zyOffice: 1.5.x
- WordPaster: 最新版
- SpringBoot: 2.7.x
- Aspose.Words: 23.10 (试用版)
希望我的经历能给同样面临这个问题的开发者一些启发和帮助!
复制插件目录
引入插件文件
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