在项目中,用到了富文本组件,编辑器中插入图片时,编辑器默认的处理是将图片转成base64保存,提交后端时请求数据太大。因此,进行改善,将图片先上传到数据库,再存储富文本数据
-
上传图片
通过
module自定义Quill的handlers,代码如下:<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; -
总结
该方法不是最好的解决方案,但是能用。