如何在 firebase 云函数上解析 multipart/form-data?
How to parse multipart/form-data on firebase cloud functions?
根据此处的文档,我一直在尝试 post 将带有文本和图像文件的 multipart/form-data object 添加到我的一个云函数中:
https://cloud.google.com/functions/docs/writing/http#multipart_data_and_file_uploads
除了我将其包装在 CORS 响应中外,我的云函数几乎与示例完全相同。看起来,无论如何,busboy 的 'field' 和 'file' 事件永远不会触发,当我打印请求 body 的 toString 方法时,我在它之前得到了一些数据变得胡言乱语。
是否可能是我在发送 FormData 时设置不正确?
这是包含我的 XMLHttpRequest() 的代码:
var formData = new FormData(document.getElementById("ticketForm"));
return new Promise(function (resolve, reject) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", "https://us-central1-XXXXXXX.cloudfunctions.net/ticketFunction");
var boundary = Math.random().toString().substr(8) + "--";
xmlhttp.setRequestHeader('Content-Type', 'multipart/form-data;charset=utf-8; boundary=' + boundary);
// xmlhttp.setRequestHeader('Content-Type', undefined);
xmlhttp.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xmlhttp.response);
} else {
reject({
status: this.status,
statusText: xmlhttp.statusText
});
}
};
xmlhttp.onerror = function () {
reject({
status: this.status,
statusText: xmlhttp.statusText
});
};
xmlhttp.send(formData);
});
这是我的云函数:
exports.newTicketWithPhoto = functions.https.onRequest((req, res) => {
cors(要求,资源,()=> {
if (req.method === 'POST') {
const busboy = new Busboy({ headers: req.headers });
const tmpdir = os.tmpdir();
console.log("Length: " + req.headers['content-length']);
console.log(req.body.toString());
// This object will accumulate all the fields, keyed by their name
const fields = {};
// This object will accumulate all the uploaded files, keyed by their name.
const uploads = {};
// This code will process each non-file field in the form.
busboy.on('field', (fieldname, val) => {
// TODO(developer): Process submitted field values here
console.log(`Processed field ${fieldname}: ${val}.`);
fields[fieldname] = val;
});
busboy.on('error', function(err){
console.log("Error: " + err);
});
// This code will process each file uploaded.
busboy.on('file', (fieldname, file, filename) => {
// Note: os.tmpdir() points to an in-memory file system on GCF
// Thus, any files in it must fit in the instance's memory.
console.log(`Processed file ${filename}`);
const filepath = path.join(tmpdir, filename);
uploads[fieldname] = filepath;
file.pipe(fs.createWriteStream(filepath));
});
// This event will be triggered after all uploaded files are saved.
busboy.on('finish', () => {
// TODO(developer): Process uploaded files here
console.log(fields);
console.log("Uploads: " + JSON.stringify(uploads));
for (const name in uploads) {
console.log(name);
const file = uploads[name];
fs.unlinkSync(file);
}
res.send();
});
req.pipe(busboy);
} else {
// Return a "method not allowed" error
res.status(405).send("Something weird happened");
}
})
});
我注意到的几件事是:
打印 header 的 content-length 值似乎总是 return 未定义。
当我打印 req.body.toString() 方法时,我得到这个:
------WebKitFormBoundaryeYZHuHsOLlohyekc
Content-Disposition: form-data; name="description"
testing description
------WebKitFormBoundaryeYZHuHsOLlohyekc
Content-Disposition: form-data; name="priority"
Low
------WebKitFormBoundaryeYZHuHsOLlohyekc
Content-Disposition: form-data; name="dueDate"
2018-07-27
------WebKitFormBoundaryeYZHuHsOLlohyekc
Content-Disposition: form-data; name="customer"
zavtra
------WebKitFormBoundaryeYZHuHsOLlohyekc
Content-Disposition: form-data; name="email"
test@test.com
------WebKitFormBoundarysseArmLvKhJY0TAm
Content-Disposition: form-data; name="photo"; filename="brighthabits1.png"
Content-Type: image/png
�PNG
IHRGB���@IDATx�}�ݴտ��I�$�V���*�EH ! �:(_7m)-ݻ�ί���{-dCaf��*�=!����N����ͽ�ږm�y�tt�OG�ʶ,6L���L*�ć[����V;�x�+[�c�/�0;@a�5��;��]]<x��\R�cqoG`rGƵ�t����O�y�J���"
����*�,�F,��.�ib�
��I�.�SV�;��h�!v��~T�EY����(u\�4+&��I��9@~wP�`N��H�;�G"7.BI��h
P��$R
�0pt,�[=��E��8����$^$��
"�,�,�4�>�Y�YY|�v3JSW��
)�q,���i>w��A��q\-
�u���ՠ�hJW�oF������W7X��]��
)#mx������&�њ�����iu���;D��ŗL��ޥh[F�8���D�^������IW��#��
�
�
�TL�n���� {�l�`h����r ��S>�[���&���_�%R8���W��mok�E����R���.]#@5������j���o���e����?Ӟ�u�Ţ�Y��5�N'�Nf��Թ#ߏ��E;�<�?^X��x�uπʭ�V??�� s�plzBǶ
我不确定是什么导致了最后所有的乱码,但只有在我上传图片时才会出现这种情况。当表单数据中没有图像时,busboy 的 'field' 事件仍然没有触发,这让我相信某些东西仍然没有被正确解析。
令人沮丧,因为我似乎完全正确地遵循了文档。
// Node.js doesn't have a built-in multipart/form-data parsing library.
// Instead, we can use the 'busboy' library from NPM to parse these requests.
const Busboy = require("busboy")
const busboy = new Busboy({ headers: request.headers })
let fields = []
busboy.on("field", (field, val) => {
console.log(`Processed field ${field}: ${val}.`)
fields[field] = val
})
busboy.end(request.rawBody)
我正在关注这个存储库 Image Upload with Busboy 并且在 Firebase Cloud 上工作得很好。
官方 google 文档建议使用 Busboy npm 包,正如接受的答案所建议的那样。将这些张贴在这里以防它们对其他人有用:
https://cloud.google.com/functions/docs/writing/http#multipart_data
首先安装一个busboy依赖
npm i busboy
从 FormData
创建函数到 return 数据后
const getFieldsFromFormData = (headers: any, body: any) =>
new Promise(async (resolve, reject) => {
const Busboy = require('busboy');
const busboy = new Busboy({ headers });
let fields: any = {};
busboy.on("field", (field: string, val: any) => fields[field] = val)
busboy.on('finish',() => resolve(fields));
busboy.end(body)
});
在控制器中
expressInstance.post('/upload', async (req, res, next) => {
const body = await getFieldsFromFormData(req.headers, req.body)
console.log('MyFormData:>> ', body);
res.send({});
})
根据此处的文档,我一直在尝试 post 将带有文本和图像文件的 multipart/form-data object 添加到我的一个云函数中:
https://cloud.google.com/functions/docs/writing/http#multipart_data_and_file_uploads
除了我将其包装在 CORS 响应中外,我的云函数几乎与示例完全相同。看起来,无论如何,busboy 的 'field' 和 'file' 事件永远不会触发,当我打印请求 body 的 toString 方法时,我在它之前得到了一些数据变得胡言乱语。
是否可能是我在发送 FormData 时设置不正确?
这是包含我的 XMLHttpRequest() 的代码:
var formData = new FormData(document.getElementById("ticketForm"));
return new Promise(function (resolve, reject) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", "https://us-central1-XXXXXXX.cloudfunctions.net/ticketFunction");
var boundary = Math.random().toString().substr(8) + "--";
xmlhttp.setRequestHeader('Content-Type', 'multipart/form-data;charset=utf-8; boundary=' + boundary);
// xmlhttp.setRequestHeader('Content-Type', undefined);
xmlhttp.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xmlhttp.response);
} else {
reject({
status: this.status,
statusText: xmlhttp.statusText
});
}
};
xmlhttp.onerror = function () {
reject({
status: this.status,
statusText: xmlhttp.statusText
});
};
xmlhttp.send(formData);
});
这是我的云函数:
exports.newTicketWithPhoto = functions.https.onRequest((req, res) => { cors(要求,资源,()=> {
if (req.method === 'POST') {
const busboy = new Busboy({ headers: req.headers });
const tmpdir = os.tmpdir();
console.log("Length: " + req.headers['content-length']);
console.log(req.body.toString());
// This object will accumulate all the fields, keyed by their name
const fields = {};
// This object will accumulate all the uploaded files, keyed by their name.
const uploads = {};
// This code will process each non-file field in the form.
busboy.on('field', (fieldname, val) => {
// TODO(developer): Process submitted field values here
console.log(`Processed field ${fieldname}: ${val}.`);
fields[fieldname] = val;
});
busboy.on('error', function(err){
console.log("Error: " + err);
});
// This code will process each file uploaded.
busboy.on('file', (fieldname, file, filename) => {
// Note: os.tmpdir() points to an in-memory file system on GCF
// Thus, any files in it must fit in the instance's memory.
console.log(`Processed file ${filename}`);
const filepath = path.join(tmpdir, filename);
uploads[fieldname] = filepath;
file.pipe(fs.createWriteStream(filepath));
});
// This event will be triggered after all uploaded files are saved.
busboy.on('finish', () => {
// TODO(developer): Process uploaded files here
console.log(fields);
console.log("Uploads: " + JSON.stringify(uploads));
for (const name in uploads) {
console.log(name);
const file = uploads[name];
fs.unlinkSync(file);
}
res.send();
});
req.pipe(busboy);
} else {
// Return a "method not allowed" error
res.status(405).send("Something weird happened");
}
}) });
我注意到的几件事是: 打印 header 的 content-length 值似乎总是 return 未定义。
当我打印 req.body.toString() 方法时,我得到这个:
------WebKitFormBoundaryeYZHuHsOLlohyekc
Content-Disposition: form-data; name="description"
testing description
------WebKitFormBoundaryeYZHuHsOLlohyekc
Content-Disposition: form-data; name="priority"
Low
------WebKitFormBoundaryeYZHuHsOLlohyekc
Content-Disposition: form-data; name="dueDate"
2018-07-27
------WebKitFormBoundaryeYZHuHsOLlohyekc
Content-Disposition: form-data; name="customer"
zavtra
------WebKitFormBoundaryeYZHuHsOLlohyekc
Content-Disposition: form-data; name="email"
test@test.com
------WebKitFormBoundarysseArmLvKhJY0TAm
Content-Disposition: form-data; name="photo"; filename="brighthabits1.png"
Content-Type: image/png
�PNG
IHRGB���@IDATx�}�ݴտ��I�$�V���*�EH ! �:(_7m)-ݻ�ί���{-dCaf��*�=!����N����ͽ�ږm�y�tt�OG�ʶ,6L���L*�ć[����V;�x�+[�c�/�0;@a�5��;��]]<x��\R�cqoG`rGƵ�t����O�y�J���"
����*�,�F,��.�ib�
��I�.�SV�;��h�!v��~T�EY����(u\�4+&��I��9@~wP�`N��H�;�G"7.BI��h
P��$R
�0pt,�[=��E��8����$^$��
"�,�,�4�>�Y�YY|�v3JSW��
)�q,���i>w��A��q\-
�u���ՠ�hJW�oF������W7X��]��
)#mx������&�њ�����iu���;D��ŗL��ޥh[F�8���D�^������IW��#��
�
�
�TL�n���� {�l�`h����r ��S>�[���&���_�%R8���W��mok�E����R���.]#@5������j���o���e����?Ӟ�u�Ţ�Y��5�N'�Nf��Թ#ߏ��E;�<�?^X��x�uπʭ�V??�� s�plzBǶ
我不确定是什么导致了最后所有的乱码,但只有在我上传图片时才会出现这种情况。当表单数据中没有图像时,busboy 的 'field' 事件仍然没有触发,这让我相信某些东西仍然没有被正确解析。
令人沮丧,因为我似乎完全正确地遵循了文档。
// Node.js doesn't have a built-in multipart/form-data parsing library.
// Instead, we can use the 'busboy' library from NPM to parse these requests.
const Busboy = require("busboy")
const busboy = new Busboy({ headers: request.headers })
let fields = []
busboy.on("field", (field, val) => {
console.log(`Processed field ${field}: ${val}.`)
fields[field] = val
})
busboy.end(request.rawBody)
我正在关注这个存储库 Image Upload with Busboy 并且在 Firebase Cloud 上工作得很好。
官方 google 文档建议使用 Busboy npm 包,正如接受的答案所建议的那样。将这些张贴在这里以防它们对其他人有用:
https://cloud.google.com/functions/docs/writing/http#multipart_data
首先安装一个busboy依赖
npm i busboy
从 FormData
创建函数到 return 数据后const getFieldsFromFormData = (headers: any, body: any) =>
new Promise(async (resolve, reject) => {
const Busboy = require('busboy');
const busboy = new Busboy({ headers });
let fields: any = {};
busboy.on("field", (field: string, val: any) => fields[field] = val)
busboy.on('finish',() => resolve(fields));
busboy.end(body)
});
在控制器中
expressInstance.post('/upload', async (req, res, next) => {
const body = await getFieldsFromFormData(req.headers, req.body)
console.log('MyFormData:>> ', body);
res.send({});
})