如何在 Cloud Functions for Firebase 上使用 express 执行 HTTP 文件上传(multer、busboy) [英] How to perform an HTTP file upload using express on Cloud Functions for Firebase (multer, busboy)

查看:23
本文介绍了如何在 Cloud Functions for Firebase 上使用 express 执行 HTTP 文件上传(multer、busboy)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将文件上传到 Cloud Functions,使用 Express 处理那里的请求,但我没有成功.我创建了一个在本地运行的版本:

I am trying to upload a file to Cloud Functions, using Express to handle requests there, but i am not succeeding. I created a version that works locally:

服务器端js

const express = require('express');
const cors = require('cors');
const fileUpload = require('express-fileupload');

const app = express();
app.use(fileUpload());
app.use(cors());

app.post('/upload', (req, res) => {
    res.send('files: ' + Object.keys(req.files).join(', '));
});

客户端js

const formData = new FormData();
Array.from(this.$refs.fileSelect.files).forEach((file, index) => {
    formData.append('sample' + index, file, 'sample');
});

axios.post(
    url,
    formData, 
    {
        headers: { 'Content-Type': 'multipart/form-data' },
    }
);

这个完全相同的代码在部署到 Cloud Functions 时似乎会中断,其中 req.files 未定义.有人知道这里发生了什么吗?

This exact same code seems to break when deployed to Cloud Functions, where req.files is undefined. Does anyone have any idea what is happening here?

编辑我也尝试过使用 multer,它在本地运行良好,但是一旦上传到 Cloud Functions,我就会得到一个空数组(相同的客户端代码):

EDIT I also had a go at using multer, which worked fine locally, but once uploaded to Cloud Functions, this got me an empty array (same clientside code):

const app = express();
const upload = multer();
app.use(cors());

app.post('/upload', upload.any(), (req, res) => {
    res.send(JSON.stringify(req.files));
});

推荐答案

确实触发此问题的 Cloud Functions 设置发生了重大变化.它与中间件的工作方式有关,该方式应用于所有用于提供 HTTPS 功能的 Express 应用程序(包括默认应用程序).基本上,Cloud Functions 将解析请求的正文并决定如何处理它,将正文的原始内容留在 req.rawBody 中的 Buffer 中.你可以用它来直接解析你的多部分内容,但你不能用中间件(比如 multer)来做.

There was indeed a breaking change in the Cloud Functions setup that triggered this issue. It has to do with the way the middleware works that gets applied to all Express apps (including the default app) used to serve HTTPS functions. Basically, Cloud Functions will parse the body of the request and decide what to do with it, leaving the raw contents of the body in a Buffer in req.rawBody. You can use this to directly parse your multipart content, but you can't do it with middleware (like multer).

相反,您可以使用名为 busboy 的模块直接处理原始正文内容.它可以接受 rawBody 缓冲区并用它找到的文件回电给你.下面是一些示例代码,它将迭代所有上传的内容,将它们保存为文件,然后删除它们.显然,您会想做一些更有用的事情.

Instead, you can use a module called busboy to deal with the raw body content directly. It can accept the rawBody buffer and will call you back with the files it found. Here is some sample code that will iterate all the uploaded content, save them as files, then delete them. You'll obviously want to do something more useful.

const path = require('path');
const os = require('os');
const fs = require('fs');
const Busboy = require('busboy');

exports.upload = functions.https.onRequest((req, res) => {
    if (req.method === 'POST') {
        const busboy = new Busboy({ headers: req.headers });
        // This object will accumulate all the uploaded files, keyed by their name
        const uploads = {}

        // This callback will be invoked for each file uploaded
        busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
            console.log(`File [${fieldname}] filename: ${filename}, encoding: ${encoding}, mimetype: ${mimetype}`);
            // Note that os.tmpdir() is an in-memory file system, so should only 
            // be used for files small enough to fit in memory.
            const filepath = path.join(os.tmpdir(), fieldname);
            uploads[fieldname] = { file: filepath }
            console.log(`Saving '${fieldname}' to ${filepath}`);
            file.pipe(fs.createWriteStream(filepath));
        });

        // This callback will be invoked after all uploaded files are saved.
        busboy.on('finish', () => {
            for (const name in uploads) {
                const upload = uploads[name];
                const file = upload.file;
                res.write(`${file}
`);
                fs.unlinkSync(file);
            }
            res.end();
        });

        // The raw bytes of the upload will be in req.rawBody.  Send it to busboy, and get
        // a callback when it's finished.
        busboy.end(req.rawBody);
    } else {
        // Client error - only support POST
        res.status(405).end();
    }
})

请记住,保存到临时空间的文件会占用内存,因此它们的总大小应限制为 10MB.对于较大的文件,您应该将它们上传到 Cloud Storage 并使用存储触发器对其进行处理.

Bear in mind that files saved to temp space occupy memory, so their sizes should be limited to a total of 10MB. For larger files, you should upload those to Cloud Storage and process them with a storage trigger.

另请记住,Cloud Functions 添加的默认中间件选择当前未通过 firebase serve 添加到本地模拟器.因此,在这种情况下,此示例将不起作用(rawBody 将不可用).

Also bear in mind that the default selection of middleware added by Cloud Functions is not currently added to the local emulator via firebase serve. So this sample will not work (rawBody won't be available) in that case.

该团队正在更新文档,以更清楚地了解与标准 Express 应用不同的 HTTPS 请求期间发生的一切.

The team is working on updating the documentation to be more clear about what all happens during HTTPS requests that's different than a standard Express app.

这篇关于如何在 Cloud Functions for Firebase 上使用 express 执行 HTTP 文件上传(multer、busboy)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆