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

如何将Editor.js生成的复杂JSON数据上传至Firebase Firestore?

问题

我正在使用React JS与Editor.js开发博客应用,Editor.js返回的数据为JSON对象。目前我已接入Firebase,想要将这些数据上传至Firestore,但该JSON结构较为复杂。

Editor.js返回的JSON示例:

{
  "time": 1683888680625,
  "blocks": [
    {
      "id": "mhTl6ghSkV",
      "type": "paragraph",
      "data": {
        "text": "Hey. Meet the new Editor. On this picture you can see it in action. Then, try a demo 🤓"
      }
    },
    {
      "id": "l98dyx3yjb",
      "type": "header",
      "data": {
        "text": "Key features",
        "level": 3
      }
    },
    {
      "id": "os_YI4eub4",
      "type": "list",
      "data": {
        "type": "unordered",
        "items": [
          "It is a block-style editor",
          "It returns clean data output in JSON",
          "Designed to be extendable and pluggable with a simple API"
        ]
      }
    },
    {
      "id": "TcUNySG15P",
      "type": "paragraph",
      "data": {
        "text": "Workspace in classic editors is made of a single contenteditable element, used to create different HTML markups. Editor.js workspace consists of separate Blocks: paragraphs, headings, images, lists, quotes, etc. Each of them is an independent <sup data-tune=\"footnotes\">1</sup> contenteditable element (or more complex structure) provided by Plugin and united by Editor's Core."
      },
      "tunes": {
        "footnotes": [
          "It works more stable then in other WYSIWYG editors. Same time it has smooth and well-known arrow navigation behavior like classic editors."
        ]
      }
    },
    {
      "id": "XKNT99-qqS",
      "type": "attaches",
      "data": {
        "file": {
          "url": "https://drive.google.com/user/catalog/my-file.pdf",
          "size": 12902,
          "name": "file.pdf",
          "extension": "pdf"
        },
        "title": "My file"
      }
    },
    {
      "id": "hZAjSnqYMX",
      "type": "image",
      "data": {
        "file": {
          "url": "assets/codex2x.png"
        },
        "withBorder": false,
        "withBackground": false,
        "stretched": true,
        "caption": "CodeX Code Camp 2019"
      }
    }
  ]
}

核心需求:先将JSON中包含的图片/文件上传至Firebase Storage,更新对应URL后,再将完整JSON上传至Firestore,请问如何实现这一流程?


解决方案

步骤1:识别需上传的资源块

遍历Editor.js返回的blocks数组,筛选出type为image或attaches的块——这两类块包含需要上传到Storage的文件资源。注意区分本地文件(用户刚选择的File对象)和已存在的线上URL(无需重复上传)。

步骤2:封装Firebase Storage上传函数

先写一个通用的文件上传函数,负责将本地文件上传到Storage并返回下载URL:

import { storage, firestore } from './firebaseConfig'; // 导入你的Firebase配置文件
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
import { doc, setDoc } from "firebase/firestore";

// 上传文件到Storage并返回下载URL
const uploadFileToStorage = async (file, storagePath) => {
  const storageRef = ref(storage, storagePath);
  await uploadBytes(storageRef, file);
  return getDownloadURL(storageRef);
};

步骤3:批量处理资源并更新JSON

遍历所有块,对包含本地文件的块执行上传操作,替换原URL为Storage返回的新URL,最后将更新后的JSON上传到Firestore:

const saveEditorData = async (editorData) => {
  // 深拷贝原数据,避免修改编辑器原始状态
  const updatedData = JSON.parse(JSON.stringify(editorData));

  // 遍历处理每个块
  for (const block of updatedData.blocks) {
    // 处理图片块
    if (block.type === 'image') {
      const file = block.data.file;
      // 判断是否为本地File对象(而非线上URL)
      if (file instanceof File) {
        // 生成唯一存储路径,避免文件名冲突
        const storagePath = `blog-images/${Date.now()}-${file.name}`;
        const newUrl = await uploadFileToStorage(file, storagePath);
        // 更新块中的文件URL
        block.data.file = { url: newUrl };
      }
    }
    // 处理附件块
    else if (block.type === 'attaches') {
      const file = block.data.file;
      if (file instanceof File) {
        const storagePath = `blog-attachments/${Date.now()}-${file.name}`;
        const newUrl = await uploadFileToStorage(file, storagePath);
        // 完整更新附件信息(URL、大小、文件名、扩展名)
        block.data.file = {
          url: newUrl,
          size: file.size,
          name: file.name,
          extension: file.name.split('.').pop()
        };
      }
    }
  }

  // 将更新后的JSON上传到Firestore
  try {
    // 生成唯一文档ID,也可自定义业务ID
    const blogDocRef = doc(firestore, 'blogs', `blog-${Date.now()}`);
    await setDoc(blogDocRef, updatedData);
    console.log('博客数据保存成功');
  } catch (err) {
    console.error('保存失败:', err);
  }
};

步骤4:绑定Editor.js保存事件

在编辑器的保存按钮点击事件中,调用上述函数:

// 假设editor是Editor.js的实例
const handleEditorSave = async () => {
  const editorData = await editor.save();
  await saveEditorData(editorData);
};

关键注意事项

  • 异步顺序控制:使用async/await确保所有文件上传完成后再更新JSON并提交到Firestore,避免数据不一致。
  • Storage权限:开发环境可临时设置Storage规则为公开读写,生产环境必须配置严格的安全规则(仅允许认证用户上传/访问)。
  • 错误处理:在上传和保存过程中添加try/catch,捕获网络异常、权限不足等错误并提示用户。
  • 重复上传判断:通过判断file是否为File对象,避免重复上传已存在的线上URL资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 00:07:11