军工项目如何解决umeditor粘贴Word图片乱码问题?
·
老张的CMS企业官网外包项目日记:给UEditor加上Word粘贴神功
Day 1:接到需求时的懵逼时刻
"什么?要在UEditor里实现Word一键粘贴?还要支持Latex公式转MathML?"我看着需求文档,感觉头发又少了几根。客户王总拍着胸脯说:“小张啊,我们公司老同志多,能直接从Word复制粘贴是最吼的!”
好吧,谁让人家是甲方爸爸呢。我摸了摸钱包里仅剩的680元预算,打开了QQ群:“各位大佬,有推荐的好用编辑器插件吗?要能完美支持Word粘贴的那种…”
Day 2:发现"新大陆" - Luckysheet+UEditor组合拳
经过三天三夜的百度和GitHub冲浪,我发现了两个宝贝:
- Luckysheet:这个国产开源表格神器居然能处理Word文档解析
- UEditor插件市场:有个叫
ueditor-word-import的插件看着不错
但是!这两个都不能完美满足需求。于是我决定:自己动手,丰衣足食!
前端实现:Vue3 + UEditor插件开发
1. 修改UEditor配置(ueditor.config.js)
// 添加自定义工具栏按钮
, toolbars: [[
// ...原有配置
, 'wordimport' // 我们的新按钮
]]
// 添加插件路径
, wordImport: {
serverUrl: '/api/editor/wordImport', // 后端接口
accept: '.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf',
maxSize: 10 // 10MB限制
}
2. 创建Word导入插件(WordImportPlugin.js)
UE.plugin.register('wordimport', function (){
var me = this;
return {
commands: {
'wordimport': {
execCommand: function () {
// 创建文件选择对话框
var fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = me.getOpt('wordImport').accept;
fileInput.onchange = function () {
if (this.files.length === 0) return;
var formData = new FormData();
formData.append('file', this.files[0]);
// 显示加载中
me.fireEvent('showmessage', {
'content': '正在解析文档...',
'timeout': 0
});
// 上传到后端处理
fetch(me.getOpt('wordImport').serverUrl, {
method: 'POST',
body: formData,
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token')
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 插入解析后的HTML
me.execCommand('inserthtml', data.content);
// 处理图片上传
if (data.images) {
data.images.forEach(img => {
me.execCommand('insertimage', {
src: img.url,
_src: img.url,
alt: img.name
});
});
}
} else {
alert('文档解析失败: ' + data.message);
}
me.fireEvent('hidemessage');
})
.catch(err => {
console.error(err);
me.fireEvent('hidemessage');
alert('上传失败,请重试');
});
};
fileInput.click();
},
queryCommandState: function () {
return me.isReady ? 0 : -1;
}
}
},
// 添加按钮到工具栏
init: function () {
me.ready(function () {
var wordImportBtn = new UE.ui.Button({
name: 'wordimport',
title: '导入Word/Excel/PPT/PDF',
onclick: function () {
me.execCommand('wordimport');
}
});
me.addListener('ready', function () {
me.ui.addToolbarButton(wordImportBtn);
});
});
}
};
});
后端实现:Java Servlet处理文档上传
1. 创建WordImportServlet.java
@WebServlet("/api/editor/wordImport")
@MultipartConfig
public class WordImportServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// OSS客户端(阿里云)
private OSS ossClient;
private String bucketName = "your-oss-bucket";
@Override
public void init() throws ServletException {
// 初始化OSS客户端
String endpoint = "oss-cn-hangzhou.aliyuncs.com";
String accessKeyId = "your-access-key";
String accessKeySecret = "your-secret-key";
ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("application/json");
PrintWriter out = resp.getWriter();
JSONObject result = new JSONObject();
try {
// 获取上传的文件
Part filePart = req.getPart("file");
if (filePart == null || filePart.getSize() == 0) {
result.put("success", false);
result.put("message", "请选择有效文件");
out.print(result.toString());
return;
}
// 检查文件大小
long maxSize = 10 * 1024 * 1024; // 10MB
if (filePart.getSize() > maxSize) {
result.put("success", false);
result.put("message", "文件大小不能超过10MB");
out.print(result.toString());
return;
}
// 获取文件信息
String fileName = filePart.getSubmittedFileName();
String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
// 临时保存文件(实际项目中可以用内存处理,这里简化)
String tempDir = System.getProperty("java.io.tmpdir");
String tempFilePath = tempDir + File.separator + UUID.randomUUID() + "." + fileExt;
filePart.write(tempFilePath);
// 处理不同类型文件
String htmlContent = "";
List imageUrls = new ArrayList<>();
switch (fileExt) {
case "doc":
case "docx":
htmlContent = processWordDocument(tempFilePath, imageUrls);
break;
case "xls":
case "xlsx":
htmlContent = processExcelDocument(tempFilePath, imageUrls);
break;
case "ppt":
case "pptx":
htmlContent = processPptDocument(tempFilePath, imageUrls);
break;
case "pdf":
htmlContent = processPdfDocument(tempFilePath, imageUrls);
break;
default:
result.put("success", false);
result.put("message", "不支持的文件类型");
out.print(result.toString());
return;
}
// 上传图片到OSS
for (String imagePath : imageUrls) {
File imgFile = new File(imagePath);
if (imgFile.exists()) {
String ossKey = "editor/images/" + UUID.randomUUID() + "." +
FilenameUtils.getExtension(imagePath);
ossClient.putObject(bucketName, ossKey, imgFile);
imgFile.delete(); // 删除临时文件
// 替换本地路径为OSS URL
String imageUrl = "https://" + bucketName + ".oss-cn-hangzhou.aliyuncs.com/" + ossKey;
htmlContent = htmlContent.replace(imagePath, imageUrl);
}
}
// 处理Latex公式转MathML
htmlContent = convertLatexToMathML(htmlContent);
result.put("success", true);
result.put("content", htmlContent);
result.put("images", new JSONArray()); // 图片URL已在内容中替换
} catch (Exception e) {
e.printStackTrace();
result.put("success", false);
result.put("message", "服务器错误: " + e.getMessage());
} finally {
out.print(result.toString());
out.flush();
}
}
}
Day 3:测试与优化
经过一番折腾,我终于实现了基本功能。测试时发现:
- Word粘贴:通过监听
paste事件实现 - 图片处理:使用Canvas提取剪贴板中的图片
- 样式保留:通过解析Word的XML结构
前端增强:添加粘贴事件处理
// 在UEditor初始化后添加
me.addListener('ready', function() {
// 监听粘贴事件
document.addEventListener('paste', function(e) {
// 检查是否是富文本编辑器内的粘贴
if (!me.isFocus()) return;
// 阻止默认粘贴行为
e.preventDefault();
// 获取剪贴板数据
var clipboardData = e.clipboardData || window.clipboardData;
if (!clipboardData) return;
// 处理文件(如从Word直接粘贴图片)
if (clipboardData.files && clipboardData.files.length > 0) {
handlePastedFiles(clipboardData.files);
return;
}
// 处理HTML内容(如从Word复制的文本)
var html = clipboardData.getData('text/html');
if (html) {
// 简单过滤Word垃圾代码
html = cleanWordHtml(html);
me.execCommand('inserthtml', html);
return;
}
// 处理纯文本
var text = clipboardData.getData('text/plain');
if (text) {
me.execCommand('inserthtml', text.replace(/\n/g, '
'));
}
});
});
Day 4:预算控制与"白嫖"技巧
看着银行卡余额,我必须精打细算:
-
开源库选择:
- Apache POI:免费处理Office文档
- PDFBox:免费处理PDF
- JLatexMath:免费Latex转MathML
-
云服务优化:
- OSS使用按量付费,预估每月费用<50元
- ECS使用1核2G突发性能实例,年费约500元
-
开发工具:
- Eclipse JEE:免费
- Vue CLI:免费
总预算控制:
- 域名:60元/年(必须)
- 服务器:500元/年
- OSS:<50元/月
- 总计:680元刚好够用!
Day 5:QQ群推广与"躺赚"计划
在测试间隙,我在QQ群(223813913)发布了插件信息:
"各位大佬,我开发了个UEditor的Word导入插件,完美支持:
- Word/Excel/PPT/PDF导入
- 图片自动上传OSS
- Latex公式转MathML
- 微信公众号内容兼容
现在招募测试员和代理商!推荐客户成交即得20%提成!"
没想到反响热烈,当天就有3个客户咨询!看来这个"躺赚"计划还真行得通…
最终效果展示

客户王总试用后:“哎呀妈呀,这功能太得劲了!我们那些老同志终于不用对着HTML代码发呆了!”
看着账户里逐渐增加的推荐提成,我仿佛看到了财务自由的曙光…(醒醒,该改bug了!)
完整项目结构
cms-project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/
│ │ │ ├── servlet/WordImportServlet.java
│ │ │ └── util/DocumentUtils.java
│ │ ├── resources/
│ │ └── webapp/
│ │ ├── WEB-INF/
│ │ ├── static/
│ │ │ ├── js/
│ │ │ │ ├── ueditor/
│ │ │ │ │ ├── ueditor.config.js
│ │ │ │ │ └── plugins/WordImportPlugin.js
│ │ │ └── css/
│ │ └── index.jsp
│ └── test/
└── pom.xml
总结
这次项目让我深刻体会到:
- 甲方需求永远比想象中复杂
- 开源库的组合使用能创造奇迹
- QQ群推广真是低成本获客利器
- 680元预算也能做出不错的产品(前提是不睡觉)
最后,欢迎各位加入QQ群223813913,一起交流技术,一起赚钱!新人加群送红包,推荐客户还有20%提成,这波不亏!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)