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

如何实现点击按钮下载Sharp处理后Buffer中的图片

实现Buffer图片前端下载功能方案

问题背景

项目中已通过Multer的multer.memoryStorage()将用户上传的图片读取到Buffer,使用Sharp完成图片格式转换后,通过.toBuffer()得到了处理完成的图片Buffer,现有上传处理逻辑调试正常,需要实现点击页面按钮触发处理后图片下载的效果。

现有代码

EJS页面代码

<form method="post" action="upload" enctype="multipart/form-data">
    <input name="image" type="file">

    <div>
        <label for="format">Select format</label>   
        <select id="format" name="format">
            <option value = "png">PNG</option> 
            <option value = "jpeg">JPEG</option>
        </select>

        <button id="submit" type="submit">Submit</button>
        <button id="download">Download</button>
    </div>
</form>

服务端server.js代码

const storage = multer.memoryStorage();
const upload = multer({storage: storage});
app.post("/upload", upload.single('image'), (req, res) => {
    (async ()=> {
        try {
            const {data, info} = await sharp(req.file.buffer)
            .withMetadata()
            .toFormat(req.body.format)
            .toBuffer({resolveWithObject: true});
        
            console.log(data, info);
        } catch(error) {
            console.log(error);
        }
    })()
});

注意:当前代码中Sharp处理生成的图片Buffer仅在/upload接口的请求生命周期内存在,请求返回后Buffer会被垃圾回收,无法跨请求留存,不能直接供后续的下载按钮调用。


实现方案

方案1:最简实现(改动量最小,无需额外依赖/前端JS)

直接在上传处理完成后返回带下载头的响应,浏览器收到响应后会自动弹出文件保存框,不需要额外写前端交互逻辑。

  1. 修改服务端/upload接口逻辑,替换原来的console.log部分,设置下载响应头后返回处理好的图片Buffer:
const storage = multer.memoryStorage();
const upload = multer({storage: storage});
app.post("/upload", upload.single('image'), (req, res) => {
    (async ()=> {
        try {
            const {data, info} = await sharp(req.file.buffer)
            .withMetadata()
            .toFormat(req.body.format)
            .toBuffer({resolveWithObject: true});
            
            // 设置下载响应头
            res.set({
                'Content-Type': `image/${req.body.format}`,
                'Content-Disposition': `attachment; filename="processed-image.${req.body.format}"`,
                'Content-Length': info.size
            });
            // 返回图片Buffer触发下载
            res.send(data);
        } catch(error) {
            console.log(error);
            res.status(500).send('图片处理失败');
        }
    })()
});
  1. 原有EJS代码不需要额外修改,表单内按钮默认type就是submit,点击按钮提交表单后,服务端处理完成会自动触发下载。

方案2:预览后下载(体验更好,无重复上传)

如果需要先提交处理、展示预览图,再点击下载按钮触发下载(不需要重复上传文件),可以通过前端JS缓存处理后的图片数据,实现无刷新交互。

  1. 修改服务端接口,处理完成后返回图片Base64数据给前端,而不是直接返回文件流:
const storage = multer.memoryStorage();
const upload = multer({storage: storage});
app.post("/upload", upload.single('image'), (req, res) => {
    (async ()=> {
        try {
            const {data, info} = await sharp(req.file.buffer)
            .withMetadata()
            .toFormat(req.body.format)
            .toBuffer({resolveWithObject: true});
            
            // 转Base64返回前端
            const base64Img = data.toString('base64');
            res.json({
                code: 0,
                imgData: `data:image/${req.body.format};base64,${base64Img}`,
                format: req.body.format
            });
        } catch(error) {
            console.log(error);
            res.status(500).json({code: -1, msg: '图片处理失败'});
        }
    })()
});
  1. 修改EJS代码,增加前端交互逻辑,拦截表单提交、缓存处理后的图片、实现下载触发:
<form id="uploadForm" method="post" action="upload" enctype="multipart/form-data">
    <input name="image" type="file">

    <div>
        <label for="format">Select format</label>   
        <select id="format" name="format">
            <option value = "png">PNG</option> 
            <option value = "jpeg">JPEG</option>
        </select>

        <button id="submit" type="submit">Submit</button>
        <button id="download" type="button" disabled>Download</button>
    </div>
    <!-- 预览区域 -->
    <div id="preview" style="margin-top: 16px;"></div>
</form>

<script>
let cachedImg = null;
let cachedFormat = null;
const form = document.getElementById('uploadForm');
const downloadBtn = document.getElementById('download');
const previewArea = document.getElementById('preview');

// 拦截表单提交
form.addEventListener('submit', async (e) => {
    e.preventDefault();
    const formData = new FormData(form);
    const resp = await fetch('/upload', {method: 'POST', body: formData});
    const result = await resp.json();
    if (result.code === 0) {
        cachedImg = result.imgData;
        cachedFormat = result.format;
        // 展示预览
        previewArea.innerHTML = `<img src="${cachedImg}" style="max-width: 400px" />`;
        // 启用下载按钮
        downloadBtn.disabled = false;
    } else {
        alert(result.msg);
    }
});

// 绑定下载事件
downloadBtn.addEventListener('click', () => {
    if (!cachedImg) return;
    const link = document.createElement('a');
    link.href = cachedImg;
    link.download = `processed-image.${cachedFormat}`;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
});
</script>

该方案下,用户点击Submit后页面无刷新,处理完成直接展示预览图,Download按钮解锁后点击即可直接下载,不需要再次向服务端发起请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:33:16