日期:2023年X月X日
角色:北京XX软件公司 全栈开发工程师(.NET Core + Vue2)
项目背景:客户要求在后台管理系统的文章发布模块新增三大功能:
- Word粘贴功能:支持从Word复制内容粘贴到UEditor,图片自动上传至内网单据存储服务器(二进制存储,非Base64),保留样式(字体、字号、颜色等)。
- 文档导入功能:支持上传Word/Excel/PPT/PDF,解析内容并保留图片和样式,渲染至UEditor。
- 微信公众号内容粘贴:支持从微信公众号编辑器复制内容(含富文本和图片)并粘贴到UEditor,图片自动上传至内网服务器。
技术栈:
- 前端:Vue2-cli + UEditor(富文本编辑器)
- 后端:.NET Core 6(C#)
- 数据库:SQL Server(存储文章元数据,图片路径存储在文件系统或对象存储)
- 服务器:内网私有部署(未来支持多云对象存储)
一、技术可行性分析与工具选型
1. Word粘贴功能
核心问题:
- 浏览器原生粘贴事件无法直接处理Word中的复杂样式(如
mso-前缀的CSS)和图片(图片以剪贴板对象或二进制流形式存在)。 - UEditor默认会过滤Word特有的HTML标签和样式,需自定义过滤规则。
- 图片需从剪贴板提取二进制数据并上传至内网服务器,生成可访问的URL。
解决方案:
- 前端:
- 使用
Clipboard API拦截粘贴事件,提取HTML和图片二进制数据(通过FileReader或FormData)。 - 调用.NET Core后端接口上传图片,替换HTML中的本地图片路径为服务器URL。
- 调整UEditor配置,保留Word相关CSS属性(如
font-family、color)。
- 使用
- 后端(.NET Core):
- 提供图片上传接口,接收二进制流并保存至内网服务器,返回访问路径。
- 支持未来迁移至对象存储(如阿里云OSS),通过依赖注入切换存储服务。
工具评估:
NPOI:开源.NET库,支持Word/Excel/PPT解析,但需手动处理样式和图片提取。OpenXML SDK:微软官方库,支持Word文档(.docx)解析,可提取图片二进制数据。iTextSharp:PDF解析库,但样式保留能力有限。Aspose.Words:商业库,支持所有格式且样式保留完整,但需授权。泽优WordPaster:开源库,支持所有格式且样式保留完整,完全开放产品源代码(下载源码)。
结论:
- Word粘贴:前端使用
Clipboard API+FormData上传图片,后端用OpenXML SDK处理复杂样式(备用方案:Aspose.Words试用版)。 - 图片存储:直接上传二进制流至服务器,避免Base64转换。
2. 文档导入功能
核心问题:
- 需支持多种格式(Word/Excel/PPT/PDF),并保留样式和图片。
- 大文件(如PPT)解析性能优化,避免阻塞主线程。
- 图片需从文档中提取为二进制并上传至服务器。
解决方案:
- 后端(.NET Core):
- Word/Excel/PPT:使用
OpenXML SDK(开源)或Aspose(商业)解析文档,提取文本、图片二进制数据和样式。 - PDF:使用
iTextSharp或PdfPig(开源)解析文本,图片需额外处理。 - 图片上传至内网服务器,生成URL替换文档中的本地路径。
- Word/Excel/PPT:使用
- 前端:
- 通过文件上传组件(如
el-upload)将文档发送至后端。 - 后端返回解析后的HTML,注入UEditor。
- 通过文件上传组件(如
工具评估:
OpenXML SDK:免费,但API复杂,需手动处理样式映射。Aspose.Total:全功能商业库,支持所有格式,但成本较高。LibreOffice:通过命令行转换文档为HTML,但需服务器安装且样式可能丢失。
结论:
- 优先方案:
OpenXML SDK(Word/Excel/PPT) +iTextSharp(PDF),样式保留不足时手动补充CSS。 - 备选方案:评估
泽优WordPaster源码版,满足公司自主安全可控需求,没有授权限制。
3. 微信公众号内容粘贴
核心问题:
- 微信公众号编辑器导出的HTML可能包含微信特有的CSS类名(如
wx_fmt_*)。 - 图片以微信CDN链接形式存在,需下载二进制数据并上传至内网服务器。
解决方案:
- 前端:
- 拦截粘贴事件,提取HTML中的微信图片URL。
- 通过.NET Core后端接口下载图片二进制数据并上传至内网服务器,替换HTML中的CDN链接。
- 后端:
- 使用
HttpClient下载微信图片,保存为二进制文件至内网服务器。
- 使用
工具评估:
HttpClient(.NET Core):标准库,支持异步下载图片。HtmlAgilityPack:解析HTML并提取图片URL。
结论:自定义实现,前端处理HTML替换,后端提供下载服务。
二、开发过程记录
1. Word粘贴功能实现
前端(Vue2 + UEditor):
// 监听UEditor粘贴事件
const editor = UE.getEditor('editor');
editor.addListener('beforePaste', function() {
// 阻止默认粘贴行为,手动处理
return false;
});
editor.addListener('ready', function() {
document.getElementById('editor').addEventListener('paste', async (e) => {
e.preventDefault();
const clipboardData = e.clipboardData || window.clipboardData;
const html = clipboardData.getData('text/html');
const files = clipboardData.files;
if (html && files.length > 0) {
const formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append('files', files[i]); // 图片二进制数据
}
// 调用后端上传图片接口
const response = await axios.post('/api/upload/word-images', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
// 替换HTML中的图片路径为服务器URL
let newHtml = html;
response.data.urls.forEach((url, index) => {
newHtml = newHtml.replace(/src=["']?([^"']*)["']?/g, (match, src) => {
if (src.startsWith('blob:') || src.startsWith('cid:')) {
return `src="${url}"`;
}
return match;
});
});
editor.setContent(newHtml); // 更新编辑器内容
}
});
});
后端(.NET Core 6):
// 图片上传接口(接收二进制流)
[ApiController]
[Route("api/upload")]
public class UploadController : ControllerBase
{
private readonly IWebHostEnvironment _env;
private readonly IConfiguration _config;
public UploadController(IWebHostEnvironment env, IConfiguration config)
{
_env = env;
_config = config;
}
[HttpPost("word-images")]
public async Task UploadWordImages(List files)
{
var uploadPath = Path.Combine(_env.WebRootPath, "uploads");
if (!Directory.Exists(uploadPath)) Directory.CreateDirectory(uploadPath);
var urls = new List();
foreach (var file in files)
{
var fileName = $"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}";
var filePath = Path.Combine(uploadPath, fileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream); // 直接保存二进制数据
}
// 返回内网可访问的URL(需配置Nginx反向代理)
var url = $"{Request.Scheme}://{Request.Host}/uploads/{fileName}";
urls.Add(url);
}
return Ok(new { urls });
}
}
2. 文档导入功能实现
后端(.NET Core 6 + OpenXML SDK):
// Word导入示例(DOCX)
[HttpPost("import/docx")]
public async Task ImportDocx(IFormFile file)
{
using (var stream = new MemoryStream())
{
await file.CopyToAsync(stream);
using (var document = WordprocessingDocument.Open(stream, false))
{
var body = document.MainDocumentPart.Document.Body;
var htmlBuilder = new StringBuilder();
foreach (var paragraph in body.Descendants())
{
htmlBuilder.Append("");
foreach (var run in paragraph.Descendants())
{
htmlBuilder.Append("");
htmlBuilder.Append(run.GetFirstChild()?.Text ?? "");
htmlBuilder.Append("");
}
// 处理段落中的图片(二进制提取)
foreach (var drawing in paragraph.Descendants())
{
var imagePart = document.MainDocumentPart.GetPartById(
drawing.Descendants().First().Embed.Value)
as ImagePart;
if (imagePart != null)
{
using (var imageStream = new MemoryStream())
{
imagePart.FeedData(imageStream);
var imageBytes = imageStream.ToArray();
// 上传图片至服务器(调用UploadController逻辑)
var imageUrl = await UploadImageToServer(imageBytes, "docx-import");
htmlBuilder.Append($"");
}
}
}
htmlBuilder.Append("");
}
return Ok(htmlBuilder.ToString());
}
}
}
// 图片上传辅助方法
private async Task UploadImageToServer(byte[] imageBytes, string prefix)
{
var fileName = $"{prefix}_{Guid.NewGuid()}.jpg";
var filePath = Path.Combine(_env.WebRootPath, "uploads", fileName);
await System.IO.File.WriteAllBytesAsync(filePath, imageBytes);
return $"{Request.Scheme}://{Request.Host}/uploads/{fileName}";
}
前端(Vue2文件上传):
export default {
methods: {
handleImportSuccess(response) {
const editor = UE.getEditor('editor');
editor.setContent(response); // 注入解析后的HTML
}
}
};
3. 微信公众号内容粘贴实现
前端(Vue2):
async function handleWechatPaste(html) {
// 提取微信图片URL(示例:src="https://mmbiz.qpic.cn/...")
const imgTags = html.match(/]+src="https:\/\/mmbiz\.qpic\.cn[^>]+>/g) || [];
for (const imgTag of imgTags) {
const url = imgTag.match(/src="([^"]+)"/)[1];
const response = await downloadFromWechat(url); // 调用后端接口
const newImgTag = imgTag.replace(url, response.url);
html = html.replace(imgTag, newImgTag);
}
const editor = UE.getEditor('editor');
editor.setContent(html);
}
async function downloadFromWechat(url) {
const response = await axios.get('/api/download/wechat-image', {
params: { url },
responseType: 'arraybuffer' // 接收二进制数据
});
// 调用图片上传接口(复用Word粘贴的上传逻辑)
const formData = new FormData();
const blob = new Blob([response.data], { type: 'image/jpeg' });
formData.append('file', blob, 'wechat-image.jpg');
const uploadResponse = await axios.post('/api/upload/word-images', formData);
return { url: uploadResponse.data.urls[0] };
}
后端(.NET Core 6):
// 下载微信图片接口
[HttpGet("download/wechat-image")]
public async Task DownloadWechatImage([FromQuery] string url)
{
using (var client = new HttpClient())
{
var response = await client.GetAsync(url);
if (!response.IsSuccessStatusCode) return BadRequest("Failed to download image");
var imageBytes = await response.Content.ReadAsByteArrayAsync();
return File(imageBytes, "image/jpeg"); // 返回二进制流
}
}
三、问题与优化
- 性能问题:
- 大文件解析(如PPT)采用异步任务队列(如Hangfire)优化。
- 样式兼容性:
- Word中的复杂样式(如文本框)需手动转换为HTML/CSS,或通过
Aspose.Words自动处理。
- Word中的复杂样式(如文本框)需手动转换为HTML/CSS,或通过
- 安全性:
- 对上传文件进行MIME类型校验,防止恶意文件上传。
- 多云支持:
- 封装存储服务接口(如
IStorageService),未来可替换为阿里云/华为云实现。
- 封装存储服务接口(如
四、总结
通过结合UEditor的扩展能力、.NET Core的OpenXML SDK和HttpClient,成功实现了客户需求。后续计划将文档解析服务拆分为独立微服务,并完善多云对象存储的适配层。
复制插件目录

引入插件文件
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转换成图片上传到服务器中。

上传网络图片

1133

被折叠的 条评论
为什么被折叠?



