[OCR] Node Express Server Example

Sample Node/Express endpoint that proxies OCR requests.

const express = require('express');
const bodyParser = require('body-parser');
const request = require('request');
const fs = require('fs');
const path = require('path');
const multer = require('multer');

const ROOT_DIR        = path.resolve(__dirname, '..');           // project root
const FILE_UPLOAD_DIR = path.resolve(ROOT_DIR, 'tmp/files');     // upload destination
const OCR_RESULT_DIR  = path.resolve(ROOT_DIR, 'tmp/work');      // processed image destination

const OCR_API_URL = '';   // OCR API URL
const OCR_API_KEY = '';   // OCR API key

const OCR_OPTIONS = {
  // 'type' is set per-request below (depends on whether fid is provided — 3.0.2403, 2.18.2403+)
  coord:         'origin',
  skew:          'image',
  boxes_type:    'all',
  save_mask:     'true',
  textout:       'true',
  recog_form:    'true',
  extract_table: 'true'
};

const app = express();
const router = express.Router();
const uploader = multer({ dest: FILE_UPLOAD_DIR });

router.post('/request', uploader.single('file'), async (req, res) => {
  const file = req.file;
  const page = (req.body || {}).page || 0;
  const fid  = req.body.fid;
  let filePathWithExt;

  if (!fid) {  // 3.0.2403, 2.18.2403+
    const originalName = file.originalname;
    const mimeType = file.mimetype;
    const extension = path.extname(originalName).split('.')[1] || mimeType.split('/')[1];
    filePathWithExt = file.path + '.' + extension;
    fs.renameSync(file.path, filePathWithExt);
  }

  try {
    const ocrResult = await requestOCR(filePathWithExt, page, fid);
    const imagePath = await downloadImage(ocrResult.result.masked_image);
    res.end(JSON.stringify({ result: ocrResult.result, imagePath }));
  } catch (error) {
    res.status(error.status).send(error.message);
  }
});

function requestOCR(imgPath, page, fid) {
  return new Promise((resolve, reject) => {
    const typeOption = {};
    if (!fid) {  // 3.0.2403, 2.18.2403+
      typeOption.type  = 'upload';
      typeOption.image = fs.createReadStream(imgPath);
    } else {
      typeOption.type = 'page';
      typeOption.fid  = fid;
    }

    const options = {
      url: `${OCR_API_URL}/ocr`,
      formData: {
        api_key: OCR_API_KEY,
        page_index: page,
        ...typeOption,
        ...OCR_OPTIONS
      }
    };

    request.post(options).on('response', response => {
      if (response.statusCode === 200) {
        let chunks = [];
        response
          .on('error', err => { throw err; })
          .on('data',  chunk => chunks.push(chunk))
          .on('end',   () => resolve(JSON.parse(Buffer.concat(chunks).toString())));
      } else {
        const err = new Error(response.statusMessage);
        err.status = response.statusCode;
        reject(err);
      }
    });
  });
}

function downloadImage(fileName) {
  return new Promise((resolve, reject) => {
    const options = {
      url: `${OCR_API_URL}/out/${fileName}`,
      formData: { api_key: OCR_API_KEY }
    };

    const imagePath = path.resolve(OCR_RESULT_DIR, fileName);
    request.post(options).on('response', response => {
      if (response.statusCode === 200) {
        let imageData = Buffer.from([]);
        response
          .on('data', chunk => { imageData = Buffer.concat([imageData, chunk]); })
          .on('end',  () => {
            fs.writeFileSync(imagePath, imageData);
            const relativeImagePath = '/' + path.relative(ROOT_DIR, imagePath).replace(/\\/g, '/');
            resolve(relativeImagePath);
          });
      } else {
        const err = new Error(response.statusMessage);
        err.status = response.statusCode;
        reject(err);
      }
    });
  });
}

app.use(bodyParser.json());
app.use('/', router);
app.listen(8080);