Search code examples
javascriptnode.jsloopback

How can I send a stream containing an image file to a remote method in loopback?


I am currently trying to produce an API using loopback that allows me to send a 28x28 image file of a handwritten character and have the image processed by a tensorflow network and return the prediction of what the network thinks the character is.

In order to do this, however, I need to be able to send the image to be processed without having to save the file on the server first and cannot find how to do such a thing. Modules such as loopback-component-storage are great, but I do not want to have to use one route to send the image, another to process that image, and then a third to then remove the container containing that image file, making the process require three different requests.

Hence it comes down to this, is there any way that I can attach an image to the request where the stream can be read and interpreted by the API without having to first save a copy of the file somewhere else on the server?

Thanks in advance


Solution

  • I am recommending the following solution:

    First, configure your server middleware to parser image request bodies:

    1. Install body-parser dependency.

      $ npm install --save body-parser
      
    2. Configure the raw parser by adding the following content to parse section of your server/middleware.json file:

      {
        "body-parser#raw": {
          "limit": "100kb",
          "type": "image/*"
        }
      }
      

      The "limit" option sets the maximum allowed request body size. You don't want to allow arbitrary size to prevent malicious clients from crashing your server on "out of memory" error.

      The "type" option configures content-types that should be parsed by this middleware. In my example above, I am allowing all image types.

    Next, implement a remote method accepting the request body. Thanks to the raw body parser, the body stream will be already converted into a Buffer for you. In my example below, I have a simple method that responds with a base64-encoded body.

    module.exports = function(Image) {
      Image.analyze = async function(data) {
        // Reject non-image requests, e.g. JSON
        if (!Buffer.isBuffer(data)) {
          const err = new Error('Unsupported media type'); 
          err.statusCode = 415;
          throw err;
        }
    
        // data is a Buffer containing the request body
        return data.toString('base64');
      };
    
      Image.remoteMethod('analyze', {
        accepts: [
          // {source: 'body'} is the important part
          {arg: 'data', type: 'object', http: {source: 'body'}},
        ],
        returns: {root: true, type: 'string'},
        http: {path: '/analyze', verb: 'post'},
      });
    };