TinyMCE-----word表格本地图片转base64并上传

有两种方法 拿到本地图片后解析成base64并上传回显 /只拿base64回显

从 Word/WPS 复制长表格到 TinyMCE 时,图片往往显示为碎图。这是因为图片的 src 指向了本地路径(如 file:///C:/...),而浏览器出于安全考虑禁止加载本地资源,控制台会报出一堆红色的 Not allowed to load local resource 错误。如果手动一张张重新上传,遇到百行长表格简直是"天塌了"的体力活,话不多说以下是我的代码 请看

一、处理图片

在 utils下 新建 rtfParser.js

javascript 复制代码
/**
 * 从 RTF 字符串中提取图片并返回 Hex 数组
 */
/**
 * 从 RTF 字符串中提取图片并返回 Hex 数组
 */
export function extractHexImagesFromRTF(rtfData) {
    
    const images = [];
    if (!rtfData) return images;
  
    // 1. 按照图片起始符切分
    const blocks = rtfData.split('\\pict');
    
    for (let i = 1; i < blocks.length; i++) {
      const block = blocks[i];
      
      // 2. 识别格式
      let mimeType = '';
      if (block.includes('\\pngblip')) mimeType = 'image/png';
      else if (block.includes('\\jpegblip')) mimeType = 'image/jpeg';
      else continue;
  
      // 3. 暴力匹配:寻找块中所有连续的 0-9a-f 字符(忽略空格和换行)
      // 我们找的是最短长度超过 128 位的字符串,这通常就是图片主体
      const allHexGroups = block.match(/[0-9a-fA-F\s\r\n]{128,}/g);
  
      if (allHexGroups) {
        // 取这一组里最长的那段(防止误删中间的控制符)
        const longestHex = allHexGroups.sort((a, b) => b.length - a.length)[0];
        const cleanHex = longestHex.replace(/[\s\r\n]/g, '');
  
        if (cleanHex.length > 256) { // 再次校验长度
          images.push({
            mimeType: mimeType,
            hex: cleanHex
          });
        }
      }
    }
    return images;
  }
  /**
   * 将 Hex 转换为 Blob
   */
  export function hexToBlob(hexString, type) {
   
    const bytes = new Uint8Array(hexString.length / 2);
    for (let i = 0; i < hexString.length; i += 2) {
      bytes[i / 2] = parseInt(hexString.substr(i, 2), 16);
    }
    return new Blob([bytes], { type });
  }

二、在tinymce中引入文件

1、import { extractHexImagesFromRTF, hexToBlob } from '@/utils/rtfParser';

方法一:解析图片并上传后台

1、修改setuo中 paste 事件中的逻辑

2、修改paste_preprocess逻辑

javascript 复制代码
// 1、修改setuo中 paste 事件中的逻辑
          ed.on('paste', (event) => {
               const rtf = event.clipboardData.getData('text/rtf');
               if (rtf && rtf.includes('\\pict')) {
                   // 快速提取 Hex 列表
                   const extracted = extractHexImagesFromRTF(rtf);

                   extracted.forEach((img, i) => {
                       // 解析单张(耗时极短,不到 100ms)
                       const base64 = _this.hexToBase64Sync(img.hex, img.mimeType);

                       // 解析完一张立刻上传一张
                       _this.uploadSingleImage(base64, i);
                   });
               }
           });

//
  paste_preprocess: function (plugin, args) {
                    let imgIdx = 0;
                    // 使用 silentPlaceholder 预先占位,避免请求图片资源时出现报错(文末附有报错截图)
                    const silentPlaceholder = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';

                    // 你的 paste_preprocess 里的替换逻辑改为:
                    let content = args.content.replace(/(<img[^>]*?)src="file:\/\/\/[^" ]*"/gi, (match, p1) => {
                        // 依然保留 data-idx,但不强制给宽高,让它沿用 Word 里的尺寸
                        return `${p1}src="${silentPlaceholder}" data-idx="${imgIdx++}"`;
                    });
                  
                    let tempDiv = document.createElement('div');
                    tempDiv.innerHTML = content;
                    if (tempDiv.querySelector('table')) {
                        console.log('粘贴的内容包含表格');
                        if (_this.type == 'table') {
                       
                            _this.$commonJS.parseTableToArray(content, (tableList) => {
                                var contentHtml = `
                            <div class="thumbnailTableBox wordTableHtml table_Box table_Box3333" style="">
                                <table border="1" style="width: auto; border-collapse: collapse; text-align: center;">
                                    ${tableList
                                        .map((row) => {
                                            return `
                                            <tr>
                                                ${row
                                                    .map((cell) => {
                                                        return `
        <td colspan="${cell.colspan || 1}" rowspan="${cell.rowspan || 1}">
            <span>${cell.text || ''}</span>
        </td>
    `;
                                                    })
                                                    .join('')}
                                            </tr>
                                        `;
                                        })
                                        .join('')}
                                </table>
                            </div>
                            `;

                                const container = document.createElement('div');
                                container.innerHTML = contentHtml;

                                // _this.updateTableStyles(container); // 根据需要应用额外的样式
                                args.content = container.innerHTML; // 更新处理后的内容
                            });
                        } else {
                        }
                    } 

                   
                    // 阻止默认的粘贴行为,确保自定义处理优先执行
                    if (args.event) {
                        args.event.preventDefault();
                        args.event.stopPropagation();
                    }

                    
                   
                },

    // 按照你要求的 XMLHttpRequest 格式编写
        uploadSingleImage(base64Data, index) {
            const _this = this;
            // 1. 将解析出来的单张 Base64 转为 Blob
            const blob = _this.dataURLtoBlob(base64Data);

            const xhr = new XMLHttpRequest();
            const formData = new FormData();

            // 按照你截图中的参数名,这里假设是 'file'
            formData.append('file', blob, `word_img_${index}.png`);
          
            xhr.withCredentials = false;
            xhr.open('POST', '接口地址');

            xhr.onload = function () {
                if (xhr.status !== 200) {
                    console.error('HTTP Error: ' + xhr.status);
                    return;
                }

                try {
                    const json = JSON.parse(xhr.responseText);
                    if (json.code === 0) {
                        // 2. 拼接服务器返回的 URL
                        const finalUrl =  json.data.url;//自己接口返回的完整 

                        // 3. 找到对应的加载中占位图并替换
                        const doc = tinymce.activeEditor.getDoc();
                        const placeholder = doc.querySelector(`img[data-idx="${index}"]`);
                        if (placeholder) {
                            placeholder.src = finalUrl;
                            placeholder.removeAttribute('data-idx'); // 任务完成,移除标记
                        }
                    }
                } catch (e) {
                    console.error('解析响应失败', e);
                }
            };

            xhr.send(formData);
        },

        // 辅助工具:Base64 转 Blob
        dataURLtoBlob(dataurl) {
            const arr = dataurl.split(','),
                mime = arr[0].match(/:(.*?);/)[1],
                bstr = atob(arr[1]);
            let n = bstr.length;
            const u8arr = new Uint8Array(n);
            while (n--) {
                u8arr[n] = bstr.charCodeAt(n);
            }
            return new Blob([u8arr], { type: mime });
        },

方法二 直接显示base64的图

在 tinymce.init 外层声明两个变量,用于存储图片数据标记

javascript 复制代码
let globalImgCounter = 0; // 放在 init 闭包内,确保计数器持续增长
let currentPasteBase64Images = [];//base64的数据

3、 在 tinymce.init setup 方法中 添加以下代码

javascript 复制代码
//
 ed.on('paste', (event) => {
     const clipboardData = event.clipboardData || window.clipboardData;
      const rtf = clipboardData.getData('text/rtf');
      if (rtf && rtf.includes('\\pict')) {
          // 同步提取 Hex
          const extracted = extractHexImagesFromRTF(rtf);
          // 【关键】同步转为 Base64 并存入闭包变量
          currentPasteBase64Images = extracted.map((img) => {
              return _this.hexToBase64Sync(img.hex, img.mimeType);
          });
      } else {
          currentPasteBase64Images = [];
      }
  });

4、和setup 同级的 paste_preprocess方法

1、使用 silentPlaceholder 预先占位,避免请求图片资源时出现报错(文末附有报错截图)

2、由于表格需要自定义样式,可以不使用默认表格渲染逻辑

javascript 复制代码
 paste_preprocess: function (plugin, args) {
           const silentPlaceholder = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
            args.content = args.content.replace(/src="file:\/\/\/[^" ]*"/gi, `src="${silentPlaceholder}"`);
                globalImgCounter = 0;
                let content = args.content; // 获取粘贴的内容
                let tempDiv = document.createElement('div');
                tempDiv.innerHTML = content;
             if (tempDiv.querySelector('table')) {
                        console.log('粘贴的内容包含表格');
                        if (_this.type == 'table') {
                            // 3. 在这里直接消费外部变量 currentPasteBase64Images
                            content = content.replace(new RegExp(`src="${silentPlaceholder}"`, 'gi'), () => {
                                // 按顺序取图
                                const base64Data = currentPasteBase64Images[globalImgCounter] || '';
                                globalImgCounter++;
                                return `src="${base64Data}"`;
                            });
                            _this.$commonJS.parseTableToArray(content, (tableList) => {
                                var contentHtml = `
                            <div class="thumbnailTableBox wordTableHtml table_Box table_Box3333" style="">
                                <table border="1" style="width: auto; border-collapse: collapse; text-align: center;">
                                    ${tableList
                                        .map((row) => {
                                            return `
                                            <tr>
                                                ${row
                                                    .map((cell) => {
                                                        return `
        <td colspan="${cell.colspan || 1}" rowspan="${cell.rowspan || 1}">
            <span>${cell.text || ''}</span>
        </td>
    `;
                                                    })
                                                    .join('')}
                                            </tr>
                                        `;
                                        })
                                        .join('')}
                                </table>
                            </div>
                            `;
                                const container = document.createElement('div');
                                container.innerHTML = contentHtml;
                                args.content = container.innerHTML; // 更新处理后的内容
                            });
                        } else {
                        }
                    }
相关推荐
0思必得09 小时前
[Web自动化] Selenium简单使用
前端·python·selenium·自动化·web自动化
2301_818732069 小时前
下载nvm后,通过nvm无法下载node,有文件夹但是为空 全局cmd,查不到node和npm 已解决
前端·npm·node.js
赵民勇9 小时前
JavaScript中的this详解(ES5/ES6)
前端·javascript·es6
hhcccchh9 小时前
学习vue第九天 计算属性与侦听器
前端·vue.js·学习
我的golang之路果然有问题9 小时前
Mac 上的 Vue 安装和配置记录
前端·javascript·vue.js·笔记·macos
代码游侠9 小时前
应用——Linux FrameBuffer图形显示与多线程消息系统项目
linux·运维·服务器·开发语言·前端·算法
小二·9 小时前
Python Web 开发进阶实战:Flask 项目中的表单验证、错误处理与用户体验优化
前端·python·flask
天荒地老笑话么9 小时前
IntelliJ IDEA 运行 Tomcat 报错:Please, configure Web Facet first!
java·前端·tomcat·intellij-idea
王五周八9 小时前
html转化为base64编码的pdf文件
前端·pdf·html