Serverless Multi-File Upload Processing

Last updated: 2021-03-19 15:15:23

    Overview

    To process an HTTP request for multipart/form-data multi-file upload through Tencent Cloud Serverless, the Base64 encoding capability of API Gateway is needed to encode the multipart byte stream in the original HTTP request into a string, so that the HTTP event can be serialized and passed to SCF for processing.

    After SCF gets and Base64-decodes the body in the event passed in by API Gateway, the generated byte stream is the same as that in a regular HTTP request and can be processed normally. In Node.js, it can be processed with libraries such as busboy.

    Directions

    Step 1. Create a function

    1. Log in to the SCF console.
    2. On the Function Service page, click Create to create a Node.js function.
      The specific parameters for creation are as follows:
    3. Click Complete.

    Step 2. Write and deploy code

    1. After creating the function, you can refer to the following sample code to write the specific logic for processing multipart/form-data.

      // handler.js
      "use strict";
      const stream = require("stream");
      const Busboy = require("busboy");
      
      /** User upload (POST) is processed */
      const handlePost = (event) => {
      return new Promise((resolve, reject) => {
       const busboy = new Busboy({ headers: event.headers });
       let html = "";
       /** The file is received */
       busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
         let buf = Buffer.alloc(0);
         console.log({ fieldname });
         /** File data blocks are received and spliced into a complete buffer */
         file.on("data", function (data) {
           buf = Buffer.concat([buf, data]);
         });
         /** File data block receipt is completed, and the DOM string is generated */
         file.on("end", function () {
           const imgBase64 = buf.toString("base64");
           html += `<img src="data:${mimetype};base64, ${imgBase64}" />`;
         });
       });
        /** multipart/form-data receipt is completed, and the generated HTML is constructed and returned */
       busboy.on("finish", function () {
         console.log({ msg: "Parse form complete!", html });
         resolve({
           statusCode: 200,
           headers: {
             "content-type": "text/html",
           },
           body: html,
         });
       });
      
       /**
        * busboy needs to process the data in the form of stream pipe.
        * After the body is decoded to the buffer,
        * it is converted into a stream and finally piped to busboy
        */
       const bodyBuf = Buffer.from(event.body, "base64");
       var bufferStream = new stream.PassThrough();
       bufferStream.end(bodyBuf);
       bufferStream.pipe(busboy);
      });
      };
      
      /** The static file is returned */
      const handleGet = (event) => {
      const html = `<html><head></head><body>
       <form method="POST" enctype="multipart/form-data">
       <input type="file" name="image-1" accept="image/*"><br />
        <input type="file" name="image-2" accept="image/*"><br />
        <input type="submit">
       </form>
       </body></html>`;
      console.log({ msg: "Get form complete!", html });
      return {
       statusCode: 200,
       headers: {
         "content-type": "text/html",
       },
       body: html,
      };
      };
      
      /** Function entry */
      exports.main_handler = async (event, context) => {
      const method = event.httpMethod;
      /** If the request is a POST request, the user's multipart/form-data is processed and a page displaying the upload result is generated */
      if (method === "POST") {
       return handlePost(event);
      }
      /** If the request is a GET request, the page where the file is uploaded is returned */
      if (method === "GET") {
       return handleGet(event);
      }
      };
    1. After writing the code, you can also install the runtime dependencies for the function. For example, you can use busboy to decode the multipart/form-data data.

    Note:

    Dependencies must be installed in the src folder.

    1. Click Deploy to complete the function deployment.

    Step 3. Bind an API Gateway trigger

    In the trigger management of the function, you need to bind an API Gateway trigger to the function before it can process users' specific HTTP requests. The specific binding method and configuration are as follows:

    At this time, if you access the link bound by API Gateway, you will find that although the static page can work, the page does not display the correct result after an image is uploaded. This is because the Base64 encoding feature is disabled in API Gateway by default. As a result, the multipart/form-data data is incorrectly encoded as a string and passed to the handler function, and busboy cannot decode it.

    Therefore, you need to enter API Gateway, find the bound API service, and enable Base64 encoding in the basic configuration.

    After the service is opened and published, it can work normally.

    Demo

    You can access the official demo built by Tencent Cloud Serverless to view the effect of file upload.