如何解析 firebase 云函数上的多部分/表单数据?

zav*_*tra 5 multipartform-data xmlhttprequest firebase busboy google-cloud-functions

根据此处的文档,我一直在尝试将带有文本和图像文件的多部分/表单数据对象发布到我的云功能之一:

https://cloud.google.com/functions/docs/writing/http#multipart_data_and_file_uploads

除了我将它包装在 CORS 响应中之外,我的云函数几乎与示例完全相同。不过,似乎无论如何,busboy 的 'field' 和 'file' 事件永远不会触发,当我打印请求正文的 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);
    });
Run Code Online (Sandbox Code Playgroud)

这是我的云功能:

export.newTicketWithPhoto = functions.https.onRequest((req, res) => { cors(req, res, () => {

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");
}
Run Code Online (Sandbox Code Playgroud)

}) });

我注意到的一些事情是:打印标题的内容长度值似乎总是返回未定义。

当我打印 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? 
Run Code Online (Sandbox Code Playgroud)

我不确定是什么导致了最后所有的胡言乱语,但这仅在我上传图像时才明确。当表单数据中没有图像时,busboy 的 'field' 事件仍然不会触发,这让我相信仍然没有正确解析某些内容。

这令人沮丧,因为否则我似乎完全正确地遵循了文档。

小智 9

<!-- language: lang-js -->
// 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)
Run Code Online (Sandbox Code Playgroud)