You need to enable JavaScript to run this app. Express开发中如何直接上传图片至Cloudinary且不存储至本地目录
优惠活动
大模型
产品
解决方案
定价
更多

Express开发中如何直接上传图片至Cloudinary且不存储至本地目录

不生成本地副本的直传实现方案

当前流程会在本地生成文件,本质是因为Multer配置了磁盘存储,默认会把上传的文件先完整写到服务器的public/uploads目录,再让后续代码读取这个本地文件传给Cloudinary。只要把Multer的存储逻辑改成内存存储,就能让上传的文件只存在于服务器内存中,完全不会写本地磁盘,改造逻辑不复杂,适配所有生产部署环境,具体改动点如下:

1. 替换Multer的存储配置

把原来的磁盘存储改成内存存储,同时补上文件大小限制,避免超大文件占满内存:

const multer = require("multer");

// 内存存储:文件直接缓存在内存,不会写入本地磁盘
const storage = multer.memoryStorage();

const fileFilter = (req, file, cb) => {
  if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
    cb(null, true);
  } else {
    cb({ message: "Unsupported file format" }, false);
  }
};

const upload = multer({
  storage: storage,
  fileFilter: fileFilter,
  // 限制单张图片最大2MB,足够dashboard场景使用
  limits: { fileSize: 2 * 1024 * 1024 }
});

module.exports = upload;

改完之后Multer会自动把文件内容存在req.file.buffer里,全程不会生成本地文件路径。

2. 改造Cloudinary上传工具

Cloudinary的SDK本身支持直接接收内存中的文件Buffer,不需要依赖本地文件路径,这里顺便修正原来Promise错误捕获的问题——之前的代码里出错直接throw,不会被Promise链正常捕获:

const cloudinary = require("../config/cloudinary");

exports.uploads = (fileBuffer, folder) => {
  return new Promise((resolve, reject) => {
    // 直接创建上传流,接收内存中的文件Buffer
    const uploadStream = cloudinary.uploader.upload_stream(
      {
        resource_type: "auto",
        folder: folder,
      },
      (err, result) => {
        if (err) return reject(err);
        resolve({
          url: result.url,
          public_id: result.public_id,
        });
      }
    );
    // 把内存中的文件内容写入上传流
    uploadStream.end(fileBuffer);
  });
};

3. 调整控制器的传参逻辑

原来传给上传函数的是本地文件路径req.file.path,现在没有本地文件了,直接把内存里的Buffer传进去就行,同时补上文件存在的校验,避免空文件报错:

const SectionType = require("../models/section-type-model");
const cloudinaryUploader = require("../utils/cloudinaryUploader");

const createSectionType = async (req, res) => {
  const body = req.body;
  // 补上必填校验:标题、描述、图片缺一不可
  if (!body?.title || !body?.description || !req.file) {
    return res.status(400).json({
      success: false,
      error: "标题、描述、图片为必填项",
    });
  }
  try {
    const uploadResult = await cloudinaryUploader.uploads(req.file.buffer, "Section-Types");
    const sectionType = new SectionType({
      title: body.title,
      description: body.description,
      image: {
        url: uploadResult.url,
        publicId: uploadResult.public_id,
      },
    });
    await sectionType.save();
    return res.status(201).json({
      success: true,
      id: sectionType._id,
      message: "Section type created!",
    });
  } catch (error) {
    return res.status(400).json({
      error,
      message: "Section type not created!",
    });
  }
};

module.exports = {
  createSectionType,
};

方案说明

  • 改造完成后,整个上传流程不会在服务器本地生成任何图片副本,完全不会占用服务器磁盘空间,哪怕部署在只读文件系统的生产平台也能正常运行
  • 去掉了本地磁盘读写的IO环节,整体上传处理速度会比原来的中转流程更快
  • 配合之前的文件类型、大小限制,内存占用完全可控,对于dashboard这类单张图片不超过几MB的常规场景,几乎不会有性能压力

如果后续需要支持超过10MB的大文件上传,内存存储的方案就不太适合了,那个场景可以用Cloudinary的客户端直传签名方案,把大文件直接从浏览器传到Cloudinary,完全跳过服务器的文件接收环节,效率更高。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.12 13:24:04