You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React集成的Tiptap编辑器中基于Image扩展实现图片异步上传?

React + Tiptap 实现异步图片上传功能

一、自定义带上传逻辑的Image扩展

基于Tiptap官方Image扩展扩展,添加上传状态管理与URL替换逻辑:

import { Image } from '@tiptap/extension-image';

export const UploadImage = Image.extend({
  addAttributes() {
    return {
      ...this.parent?.(),
      // 新增属性标记是否正在上传
      uploading: {
        default: false,
        parseHTML: element => element.hasAttribute('data-uploading'),
        renderHTML: attributes => {
          if (attributes.uploading) {
            return { 'data-uploading': '' };
          }
          return {};
        },
      },
    };
  },

  addCommands() {
    return {
      ...this.parent?.(),
      // 自定义插入图片的命令,支持传入文件对象
      insertUploadImage: (options) => ({ commands }) => {
        if (!options.file) return false;
        
        // 第一步:将文件转为base64插入编辑器
        const reader = new FileReader();
        reader.onload = (e) => {
          const base64Src = e.target.result;
          commands.insertImage({
            src: base64Src,
            uploading: true,
            alt: options.file.name,
          });

          // 第二步:异步上传图片,不阻塞用户操作
          uploadImageToServer(options.file)
            .then((uploadedUrl) => {
              // 第三步:上传完成后替换base64为真实URL
              commands.updateAttributes('image', {
                src: uploadedUrl,
                uploading: false,
              });
            })
            .catch((err) => {
              // 上传失败添加错误标记
              console.error('图片上传失败:', err);
              commands.updateAttributes('image', {
                uploading: false,
                'data-error': 'upload-failed',
              });
            });
        };
        reader.readAsDataURL(options.file);
        
        return true;
      },
    };
  },
});

// 替换为你的真实后台上传接口
async function uploadImageToServer(file) {
  const formData = new FormData();
  formData.append('image', file);
  
  const response = await fetch('/api/upload-image', {
    method: 'POST',
    body: formData,
  });
  
  if (!response.ok) throw new Error('上传失败');
  const data = await response.json();
  return data.url;
}

二、在React组件中集成使用

在编辑器组件里注册自定义扩展,添加文件选择触发逻辑:

import { useEditor, EditorContent } from '@tiptap/react';
import { UploadImage } from './UploadImage';
import StarterKit from '@tiptap/starter-kit';

export default function TextEditor() {
  const editor = useEditor({
    extensions: [
      StarterKit,
      UploadImage, // 注册自定义图片扩展
    ],
    content: '',
  });

  // 处理文件选择
  const handleImageUpload = (e) => {
    const file = e.target.files?.[0];
    if (!file || !editor) return;
    
    // 调用自定义命令插入图片并触发上传
    editor.commands.insertUploadImage({ file });
    // 重置文件输入框
    e.target.value = '';
  };

  return (
    <div>
      <button onClick={() => document.getElementById('image-upload').click()}>
        插入图片
      </button>
      <input
        id="image-upload"
        type="file"
        accept="image/*"
        onChange={handleImageUpload}
        style={{ display: 'none' }}
      />
      <EditorContent editor={editor} />
    </div>
  );
}

三、可选:添加上传状态样式

通过CSS给上传中/失败的图片添加视觉提示:

img[data-uploading] {
  opacity: 0.7;
  position: relative;
}

img[data-uploading]::after {
  content: '上传中...';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: rgba(0,0,0,0.5);
  color: white;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
}

img[data-error="upload-failed"] {
  border: 2px solid #ff4444;
}

关键逻辑说明

  • 插入图片时通过FileReader快速转base64插入,避免用户等待
  • 异步执行上传请求,不阻塞编辑器其他操作
  • 上传完成后调用updateAttributes替换图片地址,实时更新内容
  • 上传失败时标记错误状态,方便用户识别处理

内容的提问来源于stack exchange,提问作者Saman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 03:22:42