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

将本地可用的Multer上传函数适配为Google Cloud Functions的Busboy实现

迁移本地文件上传函数到Google Cloud Functions的解决方案

问题背景

我在本地使用multer的memoryStorage实现了以下文件上传代码,运行正常,但迁移到Google Cloud Functions时遇到问题,尝试用busboy但理不清思路,有两个疑问:

  1. 直接用req.rawBody替代file.buffer可行吗?
  2. 网上建议用os和fs,不知道原因是什么?

本地代码如下:

上传函数

async function uploadFile(file: any, fileName: string, targetFolder: string) {
  return new Promise((resolve, reject) => {
    const destinationFilepath: string = `${targetFolder ? targetFolder + '/' : ''}${fileName}`;
    const storage = new Storage();

    const bucket = storage.bucket(UPLOAD_BUCKET);
    const blob = bucket.file(destinationFilepath);
    const blobStream = blob.createWriteStream();

    blobStream.on('error', (err: Error) => reject(err));

    blobStream.on('finish', () => {
      resolve({ message: `Success! File uploaded to ${blob.publicUrl()}`, publicUrl: blob.publicUrl(), bucket: bucket.name, fileName: blob.name });
    });

    blobStream.end(file.buffer);
  });
}

Express POST路由

app.post('/gcs/upload/:folder?', async (req: Request, res: Response) => {
  const folder: string = req.params.folder || '';
  const fileName: string =  req?.file?.originalname ||  'FileWithoutFileName.txt';

  res.status(200).send(await uploadFile(req.rawBody, fileName, folder));
});

解决方案

1. 核心原理

Google Cloud Functions的请求处理逻辑和本地Express存在差异,multer的memoryStorage无法直接适配GCF的请求对象,必须用busboy来解析multipart/form-data类型的请求(文件上传的标准格式)。

2. 具体修改步骤

(1)安装依赖

先安装busboy包:

npm install busboy

(2)复用并优化上传函数

你的uploadFile函数几乎不需要改动,仅需调整类型定义让逻辑更严谨:

import { Storage } from '@google-cloud/storage';

// 替换为你的实际存储桶名称
const UPLOAD_BUCKET = 'your-bucket-name';

async function uploadFile(fileBuffer: Buffer, fileName: string, targetFolder: string) {
  return new Promise((resolve, reject) => {
    const destinationFilepath = `${targetFolder ? `${targetFolder}/` : ''}${fileName}`;
    const storage = new Storage();

    const bucket = storage.bucket(UPLOAD_BUCKET);
    const blob = bucket.file(destinationFilepath);
    const blobStream = blob.createWriteStream();

    blobStream.on('error', (err: Error) => reject(err));

    blobStream.on('finish', () => {
      resolve({ 
        message: `Success! File uploaded to ${blob.publicUrl()}`, 
        publicUrl: blob.publicUrl(), 
        bucket: bucket.name, 
        fileName: blob.name 
      });
    });

    blobStream.end(fileBuffer);
  });
}

(3)修改GCF请求处理函数

替换原Express路由,改为GCF的导出函数,用busboy解析请求:

import * as Busboy from 'busboy';
import { Request, Response } from 'express';

export const uploadToGCS = async (req: Request, res: Response) => {
  // 拦截非POST请求
  if (req.method !== 'POST') {
    return res.status(405).send('Method Not Allowed');
  }

  const busboy = new Busboy({ headers: req.headers });
  const folder = req.params.folder || '';
  let uploadedFile: { buffer: Buffer, filename: string };

  // 解析上传的文件字段
  busboy.on('file', (fieldname, file, filename) => {
    const chunks: Buffer[] = [];
    file.on('data', (chunk) => chunks.push(chunk));
    file.on('end', () => {
      uploadedFile = {
        buffer: Buffer.concat(chunks),
        filename: filename || 'FileWithoutFileName.txt'
      };
    });
  });

  // 解析完成后执行上传逻辑
  busboy.on('finish', async () => {
    if (!uploadedFile) {
      return res.status(400).send('No file uploaded');
    }

    try {
      const result = await uploadFile(uploadedFile.buffer, uploadedFile.filename, folder);
      res.status(200).send(result);
    } catch (err) {
      res.status(500).send(`Upload failed: ${err.message}`);
    }
  });

  // 启动请求体解析
  busboy.end(req.rawBody);
};

疑问解答

  1. 直接用req.rawBody可行吗?
    不行。multipart/form-data类型的请求体包含分隔符(boundary)、文件头和文件内容等额外信息,req.rawBody是整个请求的原始二进制数据,直接写入GCS会导致文件包含冗余信息而损坏,必须通过busboy解析后才能得到纯净的文件buffer。

  2. 为什么网上建议用os和fs?
    这类方案是把文件先写入GCF的临时目录(os.tmpdir()),再通过fs读取后上传,适合处理超大文件(避免内存溢出)。但如果你的文件体积不大,直接用内存buffer更高效,无需写入本地文件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 14:02:51