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

Express实现文件直传FTP报错求助:storageErrors为空

文件直传FTP报错排查(Express+multer-sftp)

需求:实现前端→后端→FTP服务器的文件直传流程,文件不落地存储在后端,基于Express框架开发。

问题:先后编写两段代码,均返回{"storageErrors":[]}错误,上传至FTP的图片仅160KB(远小于原文件),已在server.ts中配置bodyParser的大小限制参数,请求排查问题。


初始代码

baseServer.post(
  "/api/upload-image",
  upload.single('file'),
  function (req: Request, res: Response, next: NextFunction) {
    uploadToFTP(req, res, next);
  }
);

function uploadToFTP(req: Request, res: Response, next: NextFunction) {
  // sftp settings
  var storage = sftpStorage({
    sftp: {
      host: process.env.FTP_URL,
      port: process.env.FTP_PORT,
      username: process.env.FTP_USERNAME,
      password: process.env.FTP_PASSWORD,
    },
    destination: function (req: Request, file: any, cb: any) {
      cb(null, ""); // designation folder in host
    },
    filename: function (req: Request, file: any, cb: any) {
      // file name settings
      cb(null, file.fieldname + "-" + Date.now());
    },
  });

  var upload = multer({ storage: storage }).single("file");

  upload(req, res, function (err: any) {
    if (err) {
      console.log("Error Occured", JSON.stringify(err));
      res.json({ error: err });
    } else {
      console.log("Files uploaded successfully");
      res.json({ success: true});
    }
  });
}

修改后的代码

import { NextFunction, Request, Response } from "express";
import { admin } from "../../middlewares/authMiddleware";
import { baseServer } from "../../server";

var multer = require("multer");
var sftpStorage = require("multer-sftp");
const upload = multer();

// sftp settings
var storage = sftpStorage({
  sftp: {
    host: process.env.FTP_URL,
    port: process.env.FTP_PORT,
    username: process.env.FTP_USERNAME,
    password: process.env.FTP_PASSWORD,
  },
  destination: function (req: Request, file: any, cb: any) {
    cb(null, ""); // designation folder in host
  },
  filename: function (req: Request, file: any, cb: any) {
    // file name settings
    cb(null, file.fieldname + "-" + Date.now());
  },
});

var uploadToFTP = multer({ storage: storage }).single('file');

baseServer.post(
  "/api/upload-image",
  upload.single("file"),
  function (req: Request, res: Response, next: NextFunction) {
    uploadToFTP(req, res, function (err: any) {
      if (err) {
        console.log("Error Occured", JSON.stringify(err));
        res.json({ error: err });
      } else {
        console.log("Files uploaded successfully");
        res.json({ success: true });
      }
    });
  }
);

server.ts代码

export const baseServer = express();
const PORT = process.env.PORT;
const bodyParser = require('body-parser');            

baseServer.use(express.json());
baseServer.use(bodyParser.json({limit:'50mb'})); 
baseServer.use(bodyParser.urlencoded({extended:true, limit:'50mb'})); 

问题原因与解决方案

核心问题

两段代码都重复调用了multer的single('file')中间件:

  1. 第一个upload.single("file")已经将前端传来的文件解析到内存中,文件流被消费;
  2. 后续再调用带sftp存储的uploadToFTP时,已经没有完整的文件流可以读取,导致上传到FTP的文件不完整(仅160KB内存残留),同时触发无具体错误的storageErrors。

另外,bodyParser的配置对multipart/form-data格式的文件上传无效,multer会自行处理该格式,所以无需依赖bodyParser的大小限制。

修正方案

直接使用带sftp存储的multer实例处理请求,去掉多余的upload.single("file")中间件,让文件通过multer-sftp流式上传至FTP,全程不落地存储在后端。

同时注意:FTP_PORT环境变量需要转为数字类型(multer-sftp的port参数要求为number,而非字符串)。

修正后的完整代码

import { NextFunction, Request, Response } from "express";
import { admin } from "../../middlewares/authMiddleware";
import { baseServer } from "../../server";

const multer = require("multer");
const sftpStorage = require("multer-sftp");

// SFTP配置
const storage = sftpStorage({
  sftp: {
    host: process.env.FTP_URL,
    port: parseInt(process.env.FTP_PORT as string, 10), // 转为数字类型
    username: process.env.FTP_USERNAME,
    password: process.env.FTP_PASSWORD,
  },
  destination: (req: Request, file: any, cb: any) => {
    cb(null, "/"); // 指定FTP根目录,根据需求修改为具体文件夹
  },
  filename: (req: Request, file: any, cb: any) => {
    // 生成唯一文件名,保留原后缀
    const uniqueName = `${file.fieldname}-${Date.now()}-${file.originalname}`;
    cb(null, uniqueName);
  },
});

// 创建带SFTP存储的multer实例,可添加文件大小限制
const uploadToFTP = multer({ 
  storage,
  limits: { fileSize: 50 * 1024 * 1024 } // 限制最大50MB
}).single("file");

// 路由配置
baseServer.post(
  "/api/upload-image",
  uploadToFTP,
  (req: Request, res: Response) => {
    console.log("文件已成功上传至FTP");
    res.json({ success: true, filename: req.file?.filename });
  }
);

额外注意事项

  • 确保FTP服务器的目标目录有写入权限;
  • 若需验证用户身份,可在路由中添加对应的认证中间件(如示例中的admin)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 03:25:28