https://gist.github.com/jasonbyrne/8dcd15701f686a4703a72f13e3f800c0

аюсь загрузить файл в Cloud Functions, используя Express для обработки запросов, но у меня ничего не получается. Я создал версию, которая работает локально:

серверная часть 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' },
    }
);

Этот точно такой же код, кажется, ломается при развертывании в облачных функциях, где req.files не определен. Кто-нибудь знает, что здесь происходит?

РЕДАКТИРОВАТЬ Я также попробовал использоватьmulter, который работал нормально локально, но после загрузки в Cloud Functions, я получил пустой массив (тот же код на стороне клиента):

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

app.post('/upload', upload.any(), (req, res) => {
    res.send(JSON.stringify(req.files));
});
 Doug Stevenson13 нояб. 2017 г., 02:17
ОК, я столкнулся с той же проблемой, что и мой многоуровневый код, который работал хорошо в прошлом. Работает нормально локально сfirebase serve, тем не мение. Будет расследовать с командой облачных функций. @Hacktisch @Eindbaas
 Doug Stevenson11 нояб. 2017 г., 21:19
Я не знаю оexpress-fileupload, но я использовалmulter модуль для успешной загрузки файлов.
 Eindbaas11 нояб. 2017 г., 22:05
Я попытался настроить что-то локально с помощью multer, и это сработало сразу (см. Редактирование в посте). Но опять же, при развертывании в Firebase я не получаю никакого результата. Я не понимаю, что я делаю неправильно.
 heychez15 нояб. 2017 г., 16:36
Я связался с firebase-support и получил ответ: «Похоже, у Cloud Functions может быть проблема с multipart / form-data. Я передам ваше дело нашим инженерам и сообщу вам, как только получу обновление."
 Eindbaas11 нояб. 2017 г., 21:21
У вас есть рабочий пример этого? Я попробовалmulter, а такжеexpress-form-data но по какой-то причине не имел успеха ни с одним из них.

Ответы на вопрос(7)

'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()
        }
    })}
 rendom01 февр. 2018 г., 06:13
Этот метод отбрасывает все строковые атрибуты, переданные в многочастной форме

пробуя каждую возможную комбинацию и все эти разные библиотеки ... только чтобы обнаружить это после исчерпания всех других вариантов.

Сочетание некоторых из вышеперечисленных решений для создания сценария с поддержкой TypeScript и промежуточного программного обеспечения здесь:

https://gist.github.com/jasonbyrne/8dcd15701f686a4703a72f13e3f800c0

вы можете локально эмулировать это поведение, выполнив следующее (очевидно, добавьте это промежуточное ПО выше, чем код 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();
    }
});

я, которые вызвали эту проблему. Это связано с тем, как работает промежуточное ПО, которое применяется ко всем приложениям Express (включая приложение по умолчанию), используемым для обслуживания функций HTTPS. По сути, облачные функции проанализируют тело запроса и решат, что с ним делать, оставив необработанное содержимое тела в буфере вreq.rawBody, Вы можете использовать это для непосредственного анализа вашего многокомпонентного контента, но вы не можете сделать это с промежуточным программным обеспечением (например, multer).

Вместо этого вы можете использовать модуль под названиемпомощник официанта иметь дело с сырым содержимым тела напрямую. Он может принять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.

 nicholas29 нояб. 2017 г., 20:08
Я пробовал ваше решение, но я пытаюсь использовать обратный вызов busboy для сбора содержимого тела. Но похожеfield событие никогда не запускается, а затемfinish также никогда не запускается, и запрос заканчивается тайм-аутом.
 Leo17 апр. 2018 г., 12:54
Я только что попробовал это решение (по рекомендации Google), но оно не работает для меня. Это не работает для меня. Вот результат: клиент (Chrome, fetch) регистрирует вызов на локальный сервер Firebase как «multipart / form-data», с файлом, который я хочу загрузить, и полем. Сервер Firebase не показывает никакого вызова «busboy.on ('file' ...)". Есть вызов "busboy.on ('finish', ...)", но массив "uploads" пуст.

ответы выше Я построил модуль 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]
  ...
})
 iBug06 февр. 2018 г., 19:02
Хотя эта ссылка может ответить на вопрос,лучше включить основные части ответа здесь и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными в случае изменения связанной страницы
 Cristóvão Trevisan15 февр. 2018 г., 01:40
Добавлен пример кода, просто сохраняя ссылки как ссылки

оказалось, что команда Firebase поместила необработанный массив multipart / form-data в req.body со своим промежуточным ПО. Если вы попробуете console.log (req.body.toString ()) ДО обработки вашего запроса с помощью multer, вы увидите ваши данные. Когда multer создает новый объект req.body, который переопределяет результирующее req, данные исчезают, и все, что мы можем получить, это пустой req.body. Надеюсь, пожарная команда сможет исправить это в ближайшее время.

 Doug Stevenson17 нояб. 2017 г., 05:28
Смотрите мой ответ для более подробной информации.

ное обеспечение, которое в конечном итоге имитирует req.files в multer, так что никаких изменений в остальной части вашего кода не будет.

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('field', (fieldname, value) => {
            req.body[fieldname] = value
        })

        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('finish', () => {
            next()
        })


        busboy.end(req.rawBody)
        req.pipe(busboy)
    } else {
        next()
    }
})}
 rendom01 февр. 2018 г., 06:36
Этот метод отбрасывает все строковые атрибуты, переданные в многочастной форме
 wcandillon09 дек. 2017 г., 14:17
Большое спасибо за этот ответ! Действительно ценю это.
 Cristóvão Trevisan06 февр. 2018 г., 18:35
Спасибо за это, я отправил это в npm, проверьте мой ответ ниже
 G. Rodriguez01 февр. 2018 г., 20:53
Да, просто добавил исправление в пример. Он в основном добавляет busboy.on ('field') и сохраняет его в OG req.body. Спасибо за ответ! @rendom
 zavtra21 июл. 2018 г., 21:48
Добавление этого кода удаляет все мои другие облачные функции. Также просто интересно, по какому URL-адресу будет публиковаться сообщение при включенной функции?

Ваш ответ на вопрос