如何使用firebase云端function上的快速上传文件

我试图上传文件到云function,使用快递处理请求,但我不成功。 我创build了一个在本地工作的版本:

serverside 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函数时似乎会中断,其中req.files未定义。 有谁知道这里发生了什么?

编辑我也有去使用multer ,在本地工作正常,但一旦上传到云function,这让我一个空arrays(客户端代码相同):

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

云function设置确实引发了这个问题。 它与中间件的工作方式有关,它适用于所有用于提供HTTPSfunction的Express应用程序(包括默认应用程序)。 基本上,云端函数将parsing请求的主体,并决定如何处理,将body的原始内容留在req.rawBody的Buffer中。 你可以使用它来直接parsing你的多部分内容,但是你不能用中间件(比如multer)来完成。

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

 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}\n`); 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。 对于较大的文件,您应该将这些file upload到云存储,并使用存储触发器处理它们。

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

该团队正在更新文档,以便更清楚地了解在HTTPS请求期间发生的事情与标准的Express应用程序不同。

我能够结合Brian和Doug的回答。 这里是我的中间件,它最终模仿了multer中的req.files,因此不会对代码的其他部分进行重大更改。

 module.exports = (path, app) => { app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: true })) app.use((req, res, next) => { if(req.rawBody === undefined && req.method === 'POST' && req.headers['content-type'].startsWith('multipart/form-data')){ getRawBody(req, { length: req.headers['content-length'], limit: '10mb', encoding: contentType.parse(req).parameters.charset }, function(err, string){ if (err) return next(err) req.rawBody = string next() }) } else { next() } }) app.use((req, res, next) => { if (req.method === 'POST' && req.headers['content-type'].startsWith('multipart/form-data')) { const busboy = new Busboy({ headers: req.headers }) let fileBuffer = new Buffer('') req.files = { file: [] } busboy.on('file', (fieldname, file, filename, encoding, mimetype) => { file.on('data', (data) => { fileBuffer = Buffer.concat([fileBuffer, data]) }) file.on('end', () => { const file_object = { fieldname, 'originalname': filename, encoding, mimetype, buffer: fileBuffer } req.files.file.push(file_object) next() }) }) busboy.end(req.rawBody) } else { next() } })} 

我几天来一直在遭受同样的问题,事实certificate,Firebase团队已经将多部分/表单数据的原始内容与其中间件放在了一起。 如果您尝试使用console.log(req.body.toString()),那么在使用multer处理您的请求之前,您将看到您的数据。 由于multer创build了一个新的req.body对象,覆盖了生成的req,数据不见了,我们只能得到一个空的req.body。 希望firebase团队能尽快纠正这个问题。

要添加到官方Cloud Function团队答案中,您可以通过执行以下操作在本地模拟此行为(显然,将这​​个中间件添加到比他们发布的busboy代码更高的地方)

 const getRawBody = require('raw-body'); const contentType = require('content-type'); app.use(function(req, res, next){ if(req.rawBody === undefined && req.method === 'POST' && req.headers['content-type'] !== undefined && req.headers['content-type'].startsWith('multipart/form-data')){ getRawBody(req, { length: req.headers['content-length'], limit: '10mb', encoding: contentType.parse(req).parameters.charset }, function(err, string){ if (err) return next(err); req.rawBody = string; next(); }); } else{ next(); } });