Чтобы добавить к официальному ответу команды 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();
}
});
Благодаря ответам выше я создал для этого модуль npm ( github )
Он работает с облачными функциями Google, просто установите его (npm install --save express-multipart-file-parser
) и используйте его так:
const fileMiddleware = require('express-multipart-file-parser')
...
app.use(fileMiddleware)
...
app.post('/file', (req, res) => {
const {
fieldname,
filename,
encoding,
mimetype,
buffer,
} = req.files[0]
...
})
Спасибо всем за помощь в этой теме. Я потратил впустую целый день, пробуя каждую возможную комбинацию и все эти разные библиотеки ... только чтобы обнаружить это после исчерпания всех других вариантов.
Объединение некоторых из приведенных выше решений для создания сценария, поддерживающего TypeScript и промежуточное программное обеспечение, здесь:
https://gist.github.com/jasonbyrne/8dcd15701f686a4703a72f13e3f800c0
Я исправил некоторые ошибки в ответе Г. Родригеса. Я добавляю событие 'field' и 'finish' для Busboy и делаю next () в событии 'finish'. Это работа для меня. Как следует:
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)
})
})
busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) {
console.log('Field [' + fieldname + ']: value: ' + inspect(val));
});
busboy.on('finish', function() {
next()
});
busboy.end(req.rawBody)
req.pipe(busboy);
} else {
next()
}
})}
В настройке Cloud Functions действительно произошли серьезные изменения, которые вызвали эту проблему. Это связано с тем, как работает промежуточное ПО, которое применяется ко всем приложениям Express (включая приложение по умолчанию), используемым для обслуживания функций HTTPS. По сути, облачные функции будут анализировать тело запроса и решать, что с ним делать, оставляя необработанное содержимое тела в буфере в req.rawBody
. Вы можете использовать это для непосредственного анализа вашего многокомпонентного контента, но вы не можете сделать это с помощью промежуточного программного обеспечения (например, multer).
Вместо этого вы можете использовать модуль под названием busboy для непосредственной работы с необработанным содержимым тела. Он может принять буфер rawBody
и перезвонит вам с найденными файлами. Вот пример кода, который будет перебирать весь загруженный контент, сохранять его как файлы, а затем удалять их. Вы, очевидно, захотите сделать что-то более полезное.
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();
}
})
Помните, что файлы, сохраненные во временном пространстве, занимают память, поэтому их размеры должны быть ограничены до 10 МБ. Для более крупных файлов вы должны загрузить их в облачное хранилище и обработать их с помощью триггера хранилища.
Также имейте в виду, что выбор промежуточного программного обеспечения по умолчанию, добавляемый Cloud Functions, в настоящее время не добавляется в локальный эмулятор через firebase serve
. Так что этот пример не будет работать (rawBody не будет доступен) в этом случае.
Команда работает над обновлением документации, чтобы лучше понять, что происходит во время запросов HTTPS, отличающихся от стандартного приложения Express.