之前系统接入了一个富文本,采用的wangeditor。本来用着挺好,但是需求无止境。copy单个图片,可以黏贴进去。单个图片+文字,不行。
复制到mac备忘录,可以复制;
复制到企微邮箱,提示需要授权;
猜测:单个图片,大小可控,允许;多个图片,大小不可控。临时存储到本地,但此时涉及到授权问题,是否有权限获取本地目录。而此目录和使用何种office、系统都有关系。
加之,浏览器自身的安全,总不能随意加载任意的目录。
原因:由于安全问题浏览器不允许粘贴 file://文件路径的图片
但前端也提供了一个可行的思路,可以自定义一个按钮,进行导入。
尝试检索该问题,共同的提到(mammoth.js)
Mammoth.js 是一个开源的 JavaScript 库,主要用于将 Microsoft Word 文档(.docx 文件)转换为 HTML、Markdown 或其他文本格式。它的核心目标是通过文档中的语义信息生成简洁、干净的 HTML,而不是完全复制文档的样式。
主要功能
文档转换:将 Word 文档转换为 HTML 或 Markdown 格式,支持标题、列表、表格、图片、链接等多种元素。
样式映射:支持自定义样式映射,可以将 Word 中的样式映射为 HTML 的样式。
在线预览:转换后的 HTML 可以嵌入网页中,实现 Word 文档的在线预览。
灵活配置:提供多种配置选项,例如在转换前对文档进行预处理。
使用场景
在网页中展示 Word 文档内容,例如用户协议、文档预览等。
将 Word 文档转换为 Markdown 或纯文本格式。
在前端项目中实现文档上传和内容展示。
优势
简洁的 HTML 输出:生成的 HTML 结构清晰,便于进一步处理。
跨平台支持:可以在浏览器和 Node.js 环境中使用。
开源且灵活:基于 BSD-2-Clause 许可协议开源,开发者可以根据需要进行定制。
Mammoth.js 是一个高效且实用的工具,特别适合需要将 Word 文档内容转换为网页格式的场景。
于是,尝试集成到我们的项目中去。接下来,主要的问题可能是:
官网扩展菜单样例:
wangEditor extend modal menu
1、引入js
下载链接,带走不谢。
https://cdn.jsdelivr.net/npm/[email protected]/mammoth.browser.min.js
2、参考官网实现按钮自定义
const toolbarConfig = {
excludeKeys:[
'fullScreen',
'emotion',
'insertImage',
'insertVideo'
],
insertKeys: {
index: 0, // 插入的位置,基于当前的 toolbarKeys
keys: ['myMenu'],
}
}
// Extend menu
class MyMenu {
constructor() {
this.title = '导入'
// this.iconSvg = ''
this.tag = 'button'
this.showModal = true
this.modalWidth = 300
}
getValue(editor) {
return ''
}
isActive(editor) {
return false // or true
}
isDisabled(editor) {
return false // or true
}
exec(editor, value) {
// do nothing 什么都不用做
}
getModalPositionNode(editor) {
return null // modal 依据选区定位
}
getModalContentElem(editor) {
const $container = $('')
const inputId = `input-${Math.random().toString(16).slice(-8)}`
const buttonId = `button-${Math.random().toString(16).slice(-8)}`
window.btnFn = () => {
// e.preventDefault()
// const text = $(`#${inputId}`).val();
// if (!text) return;
convertWord(inputId);
editor.restoreSelection(); // 恢复选区
// editor.insertText(text);
// editor.insertText(' ');
}
const $inputContainer = $(``)
const $buttonContainer = $(`
`)
$container.append($inputContainer).append($buttonContainer)
setTimeout(() => {
$(`#${inputId}`).focus()
})
return $container[0]
}
}
const myMenuConf = {
key: 'myMenu',
factory() {
return new MyMenu()
}
}
window.wangEditor.Boot.registerMenu(myMenuConf);
3、使用mammoth
/**
* 转换为html 并设置到文本框
* @param inputId
*/
function convertWord(inputId) {
const input = document.getElementById(inputId);
const file = input.files[0];
if(!notNull(file)){
Message.info('附件为空');
return;
}
const reader = new FileReader();
reader.onload = (evt) => {
mammoth.convertToHtml({ arrayBuffer: evt.target.result })
.then((result) => {
const html = result.value;
editor.dangerouslyInsertHtml(html); // 将 HTML 插入到编辑器中
});
};
reader.readAsArrayBuffer(file);
}
注,网站示例是参入文本,使用:editor.insertText(text),而此处是使用:editor.dangerouslyInsertHtml(html);
具体使用,可参考:编辑器 API | wangEditor
4、验证保存后,是否回显是否正常
5.1、click事件无法绑定
getModalContentElem(editor) {
const $container = $('')
const inputId = `input-${Math.random().toString(16).slice(-8)}`
const buttonId = `button-${Math.random().toString(16).slice(-8)}`
const $inputContainer = $(``)
const $buttonContainer = $(`
`)
$container.append($inputContainer).append($buttonContainer)
$container.on('click', `#${buttonId}`, e => {
e.preventDefault()
const text = $(`#${inputId}`).val()
if (!text) return
editor.restoreSelection() // 恢复选区
editor.insertText(text)
editor.insertText(' ')
})
setTimeout(() => {
$(`#${inputId}`).focus()
})
return $container[0]
// PS:也可以把 $container 缓存下来,这样不用每次重复创建、重复绑定事件,优化性能
}
}
5.2、Form is larger than max length 200000 idea jetty
2025-01-20 10:56:24.905 ERROR [qtp517268856-136]com.chaboshi.passport.client.interceptors.ClientCertificationInterceptor(123) | 权限拦截器发生异常
org.eclipse.jetty.http.BadMessageException: 400: Unable to parse form content
at org.eclipse.jetty.server.Request.getParameters(Request.java:434)
at org.eclipse.jetty.server.Request.getParameter(Request.java:1059)
at com.chaboshi.passport.common.utils.URLParamHelper.getString(URLParamHelper.java:22)
at com.chaboshi.passport.client.interceptors.ClientCertificationInterceptor.before(ClientCertificationInterceptor.java:41)
at com.chaboshi.wf.mvc.InterceptorHandler.excuteActionBeforeInterceptors(InterceptorHandler.java:61)
at com.chaboshi.wf.mvc.action.MethodAction.invoke(MethodAction.java:191)
at com.chaboshi.wf.mvc.Dispatcher.service(Dispatcher.java:34)
at com.chaboshi.wf.mvc.WFBootstrap.doFilter(WFBootstrap.java:64)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1618)
at com.chaboshi.web.qa.filter.DMaskingFilter.doFilter(DMaskingFilter.java:93)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1610)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:549)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:602)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1610)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1369)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:489)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1580)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1284)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:191)
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:146)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
at org.eclipse.jetty.server.Server.handle(Server.java:501)
at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383)
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:556)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:272)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:375)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: Form is larger than max length 200000
at org.eclipse.jetty.server.Request.extractFormParameters(Request.java:562)
at org.eclipse.jetty.server.Request.extractContentParameters(Request.java:519)
at org.eclipse.jetty.server.Request.getParameters(Request.java:430)
... 43 more
2025-01-20 10:56:24.911 INFO [qtp517268856-136]STATS(28) | 1Min average time: 1078, qps: 0.15, concurrent: 0.17, total time: 19408ms, request count: 18.
2025-01-20 10:57:09.019 INFO [statis]com.chaboshi.wf.mvc.toolbox.metrics.MetricsStatis(46) | HeapUsage : 27.05%, HeapUsed : 984.00M, HeapMax : 3641.00M
Caused by: java.lang.IllegalStateException: Form is larger than max length 200000 idea jetty 启动解决-CSDN博客
5.3、调整数据库长度
org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'article_content' at row 1
### The error may involve com.chaboshi.web.qa.dao.detection.detectionTemplateV2.DetectionV2ComponentLocationPictureStoreDao.saveOrUpdateAll-Inline
### The error occurred while setting parameters
### SQL: UPDATE t_detection_v2_component_location_picture_store SET article_title = ?, add_time = ?, model_id = ?, article_content = ?, update_time = ?, pic = ? WHERE ( id = ?)
### Cause: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'article_content' at row 1
alter table xx
modify article_content longtext null comment '调整为 longtext |20250120|XX。';
5.4、待定.......(估计还有问题)
在 MySQL 中,TEXT
类型是一组用于存储变长文本数据的数据类型,根据存储容量的不同,分为 TINYTEXT
、TEXT
、MEDIUMTEXT
和 LONGTEXT
。以下是每种类型的特点和最大存储容量:
1. TINYTEXT
2. TEXT
3. MEDIUMTEXT
4. LONGTEXT
字符集对存储容量的影响
存储容量不仅取决于数据类型,还与字符集有关。以下是不同字符集下的最大字符数量:
TEXT
最多存储约 21,845 个字符。MEDIUMTEXT
最多存储约 5,592,405 个字符。LONGTEXT
最多存储约 1,431,655,765 个字符。TEXT
最多存储约 16,383 个字符。MEDIUMTEXT
最多存储约 4,194,303 个字符。LONGTEXT
最多存储约 1,073,741,823 个字符。TEXT
最多存储 65,535 个字符。MEDIUMTEXT
最多存储 16,777,215 个字符。LONGTEXT
最多存储 4,294,967,295 个字符。选择合适的 TEXT
类型
在选择 TEXT
类型时,应根据实际需求选择合适的数据类型:
TINYTEXT
。TEXT
。MEDIUMTEXT
。LONGTEXT
。