Markdown编辑器上传图片后路径显示404怎么办?

宇文世杰 阅读 46

在用Quill.js实现Markdown编辑器时,用户上传图片用FileSaver保存为base64格式,但渲染后图片路径显示404错误。明明保存成功了啊,控制台提示GET blob:null/xxx.jpg 404 (NOT FOUND)

尝试过直接把base64字符串塞进标签,但Markdown渲染后自动转成普通路径。查了文档发现需要自定义Blot,但不知道怎么正确绑定本地文件路径。难道要改marked.js的解析规则吗?

测试代码大概是这样:


const ImageBlot = Quill.import('blots/image');
class Base64Image extends ImageBlot {
  static create(value) {
    const img = super.create();
    img.src = value; // 这里直接赋base64字符串
    return img;
  }
}
Quill.register(Base64Image);

但保存后生成的Markdown内容变成![image](blob:null/xxx.jpg),刷新页面就找不到了,这明明是临时对象的问题吧?

我来解答 赞 10 收藏
二维码
手机扫码查看
2 条解答
迷人的雨涵
这是典型的Blob URL生命周期问题,临时URL在页面刷新后就失效了。base64方案是对的,但问题出在Markdown转换环节。

别动marked.js,直接在Quill里搞定就行。你这Blot写法基本正确,但需要两处关键修改:

1. 保存时把base64塞进Delta而不是生成临时URL
2. 渲染时确保Delta里的base64被正确还原

改后的核心代码:

class Base64Image extends ImageBlot {
static create(value) {
// 如果是上传的base64直接使用
if (value.startsWith('data:')) {
const img = super.create();
img.src = value;
return img;
}
// 兼容从Delta加载的情况
return super.create(value);
}

static value(node) {
// 重点!把base64作为值保存到Delta里
return node.getAttribute('src') || '';
}
}


上传图片时这么处理:

const reader = new FileReader();
reader.onload = (e) => {
const range = quill.getSelection();
quill.updateContents(
new Delta()
.retain(range.index)
.delete(range.length)
.insert({ image: e.target.result }) // 直接插base64
);
};
reader.readAsDataURL(file);


这样保存的Markdown内容会是完整base64格式,刷新也不怕。注意大图要压缩,不然数据库会被撑爆,我吃过这亏。

另外提个醒,如果用Quill的clipboard处理粘贴图片,记得在matchers里也加上base64判断,不然还是会转成Blob URL。
点赞
2026-03-06 20:10
Des.沁仪
这个问题的根本原因是浏览器的blob对象是临时的,页面刷新后就失效了,所以你看到404错误。要解决这个问题,建议把图片上传到服务端存储,而不是依赖前端的临时blob对象。

具体做法是这样:首先在服务端搭一个图片上传接口,可以用类似AWS S3或者本地存储来存图片。然后修改你的Quill配置,添加一个自定义的图片处理逻辑。


const quill = new Quill('#editor', {
modules: {
toolbar: {
handlers: {
image: imageHandler
}
}
}
});

function imageHandler() {
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('image', file);

// 这里调用服务端上传接口
const res = await fetch('/api/upload-image', {
method: 'POST',
body: formData
});
const data = await res.json();

// 拿到服务端返回的图片url
const imageUrl = data.url;

const range = this.quill.getSelection();
this.quill.insertEmbed(range.index, 'image', imageUrl);
};
}


服务端这边需要写个简单的上传接口,以Node.js为例:


const express = require('express');
const multer = require('multer');
const path = require('path');

const app = express();
const upload = multer({ dest: 'uploads/' });

app.post('/api/upload-image', upload.single('image'), (req, res) => {
const tempPath = req.file.path;
const targetPath = path.join(__dirname, 'public/images', req.file.originalname);

// 把临时文件移动到永久存储位置
fs.rename(tempPath, targetPath, (err) => {
if (err) return res.status(500).json({ error: 'File upload failed' });

// 返回图片的访问地址
const imageUrl = /images/${req.file.originalname};
res.json({ url: imageUrl });
});
});


记得在服务端配置静态资源目录,让图片能被正常访问。这样做完之后,图片就会以永久URL的形式保存在Markdown内容里,刷新页面也不会丢失了。虽然看起来改动有点多,但这是最靠谱的解决方案。直接用base64确实省事,但对性能和数据库都不友好,特别是大图的时候。
点赞 11
2026-02-15 16:00