react-quill 上传复制图片转为url存储后台

2,877 阅读2分钟

​ 在项目中,用到了富文本组件,编辑器中插入图片时,编辑器默认的处理是将图片转成base64保存,提交后端时请求数据太大。因此,进行改善,将图片先上传到数据库,再存储富文本数据

  • 上传图片

    通过module自定义Quillhandlers,代码如下:

          <ReactQuill
            modules={{
              toolbar: {
                handlers: {
                    image:uploadImage
                },
              },
            }}
    
            theme="snow"
            ref={v => (this.instance = v)}
          />
    

    uploadImage上传图片到服务器,并重新处理的图片,代码如下:

    const uploadImage=() => {
          const quillEditor = this.instance.getEditor();
          const input = document.createElement('input');
          input.setAttribute('type', 'file');
          input.setAttribute('accept', 'image/*');
          input.click();
          input.onchange = async () => {
            const file = input.files[0];
            const formData = new FormData();
            formData.append('quill-image', file);
            try {
              await uploadImage('image/upload', formData, function(data) { // 上传图片
                if (data.status === 200) {
                  const range = quillEditor.getSelection();
                  const link = data.result[0]; // 图片地址
                  quillEditor.insertEmbed(range.index, 'image', link); // 插入图片
                  message.success('上传图片成功');
                } else {
                  message.error('上传图片失败');
                }
              });
            } catch (e) {
              console.log(e);
              message.error('上传图片失败');
            }
          };
        }
    

    这是点击上传图片时的处理,但是在进行复制粘贴图片时,不走该方法。

  • 复制粘贴上传图片

    实现思路是监听富文本的值改变时,通过正则去匹配是否含有图片,

    /<img [^>]*src=['"]data:image([^'"]+)[^>]*>/gi //匹配base64的字符串
    

    在匹配到含有base64的数据,取到base64的数据,存入state中,然后先替换成一个标志位,在base64上传成功后再通过正则匹配标志位进行替换。为了提交复用性,可以封装成 useHooks代码如下

    import { useEffect, useState } from 'react';
    import { uploadImage } from 'services/uploadImage';
    import { message } from 'antd';
    import uuid from 'uuid/v4';
    
    // base64 转换 blob
    const dataURLtoBlob = dataurl => {
      const arr = dataurl.split(',');
      const mime = arr[0].match(/:(.\*?);/)[1];
      const 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 });
    };
    const useRichTextUploadPicture = value => {
      const [replaceValue, setReplaceValue] = useState('');
      const [imgUrls, setImgUrls] = useState({}); // 记录富文本的图片
    
      useEffect(() => {
        setReplaceValue(value);
      }, [value]);
    
      useEffect(() => {
        if (!replaceValue) return;
        // content 是要替换的字符串
        const newContent = replaceValue.replace(
          /<img [^>]\*src=['"]data:image([^'"]+)[^>]\*>/gi,
          (match, capture) => {
            const id = uuid();
            const base64Url = match.substring(10, match.length - 2);
            imgUrls[id] = { id, base64Url };
            setImgUrls({ ...imgUrls });
            return '<img src="' + id + '"/>';
          }
        );
        setReplaceValue(newContent || replaceValue);
      }, [replaceValue, imgUrls]);
    
      // 上传图片成功后进行替换
      useEffect(() => {
        const ids = Object.keys(imgUrls);
        if (!ids.length) return;
        ids.forEach(id => {
          if (imgUrls[id]?.value && !imgUrls[id]?.base64Url) {
            setReplaceValue(replaceValue.replace(id, imgUrls[id].value));
          }
        });
      }, [imgUrls]);
    
      // 进行图片上传
      useEffect(() => {
        const ids = Object.keys(imgUrls);
        if (!ids.length) return;
        try {
          ids.forEach(async id => {
            if (imgUrls[id]?.base64Url) {
              var formData = new FormData();
              const fileImg = dataURLtoBlob(imgUrls[id]?.base64Url);
              formData.append('file', fileImg, 'image.jpg');
              try {
                uploadImage('image/upload', formData, data => {
                  if (data.status === 200) {
                    imgUrls[id].value = data.result[0];
                    imgUrls[id].base64Url = undefined;
                    setImgUrls({ ...imgUrls });
                  } else {
                    message.error('上传图片失败');
                  }
                });
              } catch (e) {
                console.log(e);
                message.error('上传图片失败');
              }
            }
          });
        } catch (e) {
          console.log(e);
        }
      }, [imgUrls]);
    
      return [replaceValue, setReplaceValue];
    };
    
    export default useRichTextUploadPicture;
    
    
  • 总结

    该方法不是最好的解决方案,但是能用。