React 富文本图片上传 OSS 并防止 Base64 图片粘贴

这是一个在富文本编辑器中实现自定义图片上传到阿里云OSS、并防止Base64图片粘贴的完整方案。

实现富文本请参考https://blog.csdn.net/qq_70172010/article/details/156090593?spm=1001.2014.3001.5501

一、自定义图片上传处理程序 (handlers.image)

主要流程:

复制代码
handlers: {
    image: async function() {
        // 1. 创建文件选择input
        const input = document.createElement('input');
        input.setAttribute('type', 'file');
        input.setAttribute('accept', 'image/*');
        input.click(); // 触发文件选择对话框

        input.onchange = async () => {
            // 2. 获取选中的文件
            const file = input.files?.[0];
            
            // 3. 从后端获取阿里云上传签名(按实际接口修改)
            const signData = await request.get("/common/getSign", {
                params: { dir: "images" }
            });

            // 4. 构建上传参数
            const timestamp = new Date().getTime();
            const fileExt = file.name.substring(file.name.lastIndexOf('.'));
            const timestampFileName = `${timestamp}${fileExt}`; // 生成唯一文件名

            // 5. 创建FormData并添加必要参数
            const formData = new FormData();
            formData.append('OSSAccessKeyId', signData.data.accessId);
            formData.append('policy', signData.data.policy);
            formData.append('signature', signData.data.signature);
            formData.append('key', `images/${timestampFileName}`); // 存储路径
            formData.append('file', file);

            // 6. 使用XMLHttpRequest上传到阿里云OSS
            const xhr = new XMLHttpRequest();
            xhr.open('POST', 'https://fs-voss.oss-cn-beijing.aliyuncs.com', true);
            xhr.send(formData);

            // 7. 上传成功后获取图片URL并插入编辑器
            const url = `https://fs-voss.oss-cn-beijing.aliyuncs.com/images/${timestampFileName}`;
            
            // 8. 将图片URL插入到Quill编辑器当前光标位置
            const quill = this.quill; // 获取Quill实例
            const range = quill.getSelection(); // 获取当前光标位置
            quill.insertEmbed(range.index, 'image', url); // 插入图片
            quill.setSelection(range.index + 1); // 光标移动到图片后面
        }
    }
}

关键点:

  • 安全性:通过后端获取签名,避免在前端暴露AccessKey

  • 文件唯一性:使用时间戳作为文件名,防止覆盖

  • 非阻塞上传:使用XMLHttpRequest支持进度监控

  • 直接插入:上传成功后直接将图片插入编辑器,无需二次操作

二、监听内容变化清理Base64图片

检测逻辑:

复制代码
useEffect(() => {
    if (value && (
        // 检测各种Base64格式
        value.includes('data:image/png;base64') ||
        value.includes('data:image/jpeg;base64') ||
        value.includes('data:image/jpg;base64') ||
        value.includes('data:image/gif;base64') ||
        value.includes('data:image/webp;base64') ||
        value.match(/src=["']data:image\/[^"']*["']/) ||
        value.match(/data:image\/[^;]*;base64,[^"'\s]*/)
    )) {
        message.warning('请使用图片上传功能上传图片,不支持直接粘贴 base64 图片');
        
        // 延迟执行确保DOM更新完成
        setTimeout(() => {
            const quillEditor = document.querySelector('.ql-editor');
            
            // 1. 找到所有图片元素
            const images = quillEditor.querySelectorAll('img');
            
            // 2. 遍历删除Base64图片
            images.forEach((img) => {
                const src = img.getAttribute('src');
                if (src && src.startsWith('data:image')) {
                    img.remove(); // 从DOM中移除
                }
            });
            
            // 3. 清理HTML内容中的Base64图片标签
            const editorContent = quillEditor.innerHTML;
            const cleanContent = editorContent.replace(
                /<img[^>]*src=["']data:image\/[^"']*["'][^>]*>/g, 
                ''
            );
            
            // 4. 更新编辑器内容
            quillEditor.innerHTML = cleanContent;
            
            // 5. 触发变更事件让ReactQuill感知变化
            const event = new Event('input', { bubbles: true });
            quillEditor.dispatchEvent(event);
        }, 50);
    }
}, [value]);

三、粘贴事件处理阻止Base64图片

双重检测机制:

复制代码
// 处理粘贴事件,阻止 base64 图片
    const handlePaste = (e: Event) => {
        const clipboardEvent = e as ClipboardEvent;
        const clipboardData = clipboardEvent.clipboardData;
        if (!clipboardData) return;

        // 检查是否有图片文件(如截图)
        const items = Array.from(clipboardData.items);
        const hasImage = items.some(item => item.type.startsWith('image/'));

        if (hasImage) {
            e.preventDefault();
            message.warning('请使用图片上传功能上传图片,不支持直接粘贴图片');
            return;
        }

        // 获取粘贴内容
        const htmlContent = clipboardData.getData('text/html');
        const textContent = clipboardData.getData('text/plain');

        // 检查HTML内容中的base64图片
        if (htmlContent && (
            htmlContent.includes('data:image/png;base64') ||
            htmlContent.includes('data:image/jpeg;base64') ||
            htmlContent.includes('data:image/jpg;base64') ||
            htmlContent.includes('data:image/gif;base64') ||
            htmlContent.includes('data:image/webp;base64') ||
            htmlContent.match(/src=["']data:image\/[^"']*["']/) ||
            htmlContent.match(/data:image\/[^;]*;base64,[^"'\s]*/)
        )) {
            e.preventDefault();
            message.warning('请使用图片上传功能上传图片,不支持直接粘贴 base64 图片');
            return;
        }

        // 检查纯文本内容中的base64图片
        if (textContent && (
            textContent.includes('data:image/png;base64') ||
            textContent.includes('data:image/jpeg;base64') ||
            textContent.includes('data:image/jpg;base64') ||
            textContent.includes('data:image/gif;base64') ||
            textContent.includes('data:image/webp;base64') ||
            textContent.match(/data:image\/[^;]*;base64,[^\s]*/)
        )) {
            e.preventDefault();
            message.warning('请使用图片上传功能上传图片,不支持直接粘贴 base64 图片');
            return;
        }
    };

// 组件挂载时添加粘贴事件监听
    useEffect(() => {
        const quillElement = document.querySelector('.ql-editor');
        if (quillElement) {
            quillElement.addEventListener('paste', handlePaste);

            return () => {
                quillElement.removeEventListener('paste', handlePaste);
            };
        }
    }, []);

四、为何这么实现?

1. 性能考虑

  • Base64图片会大幅增加HTML体积

  • 直接存储在数据库会导致数据库膨胀

  • 影响页面加载速度

2. 成本考虑

  • OSS存储成本远低于数据库存储

  • CDN加速提高图片访问速度

  • 便于图片管理和清理

3. 用户体验

  • 统一的图片管理

  • 支持图片压缩、格式转换等处理

  • 防止因粘贴大图导致编辑器卡顿

4. 安全性

  • 防止恶意上传超大Base64图片

  • 控制图片格式和大小

  • 通过签名机制保证上传安全

五、整体流程图

复制代码
用户点击图片按钮 → 弹出文件选择 → 选择图片 → 
获取OSS签名 → 上传到阿里云 → 返回URL → 
插入编辑器 → 保存时存储URL

用户粘贴内容 → 检测Base64 → 阻止粘贴 → 
显示提示 → 清理已粘贴内容 → 引导使用上传功能

正常上传流程:

粘贴复制流程(清空复制的图片内容):

该方案实现了既提供便捷的上传功能,又防止Base64图片污染内容的双重目标。

注意⚠:其他验证及交互逻辑可自行修改!!!

相关推荐
佛系打工仔2 小时前
绘制K线第二章:背景网格绘制
android·前端·架构
明天好,会的4 小时前
分形生成实验(五):人机协同破局--30万token揭示Actix-web状态管理的微妙边界
运维·服务器·前端
C_心欲无痕5 小时前
nginx - alias 和 root 的区别详解
运维·前端·nginx
我是苏苏7 小时前
Web开发:C#通过ProcessStartInfo动态调用执行Python脚本
java·服务器·前端
无羡仙7 小时前
Vue插槽
前端·vue.js
哈__7 小时前
React Native 鸿蒙跨平台开发:PixelRatio 像素适配
javascript·react native·react.js
用户6387994773058 小时前
每组件(Per-Component)与集中式(Centralized)i18n
前端·javascript
SsunmdayKT8 小时前
React + Ts eslint配置
前端
开始学java8 小时前
useEffect 空依赖 + 定时器 = 闭包陷阱?count 永远停在 1 的坑我踩透了
前端