Google Cloud Functions に ファイルをアップロードして Vision API で OCR
Google Cloud Functions へファイルをアップロードしGoogle Cloud Storage へ保存できたので、アップロードしたので、そのファイルを Cloud Vision APIにかけてOCRを行う。
Firebase Admin SDK を利用しているので、Google Cloud Platform 周りの認証が隠蔽されるので非常に便利。
以下の点にはまったが、なんとか夏季休暇2日目に疎通できた。
- Functions呼び出しURLを間違えていることに気づかず、Vision APIは、Firebase Admin SDK が利用できないと勘違いし空回り。結局利用できた。
- TypeScript と import を使用したかったのだが、tsc のエラーが解決できなかったので、コーディングでは、import を使用して、型定義の恩恵をえて、デプロイ時にはtscエラーを回避するために、require に切り替えるなど。
1.Functions 実装
functions/index.ts
import * as functions from ‘firebase-functions’; import * as admin from ‘firebase-admin’; admin.initializeApp(); import * as path from ‘path’; import * as os from ‘os’; import * as fs from ‘fs’; import * as Busboy from ‘busboy’;
// コーディング時にTypeScriptの型定義を import して利用したいが、
// デプロイではエラーになるので、require を使う
//import { v1p1beta1 as vision } from ‘@google-cloud/vision’;
const vision = require(“@google-cloud/vision”).v1p1beta1;
export const helloVisions = functions.https.onRequest((request, response) => {
if (request.method !== ‘POST’) {
response.status(405).end();
return;
}
const visionClient = new vision.ImageAnnotatorClient();
const busboy = new Busboy({headers: request.headers});
const tmpdir = os.tmpdir();
const uploads: {[key:string]:string} = {};
const fileWrites: Array<Promise<any>> = [];
busboy.on('file', (fieldname, file, filename) => {
console.log(`Processed file ${filename}`);
const filepath = path.join(tmpdir, filename);
console.log(`Filepath ${filepath}`);
uploads[fieldname] = filepath;
const writeStream = fs.createWriteStream(filepath);
file.pipe(writeStream);
const promise = new Promise((resolve, reject) => {
file.on('end', () => {
writeStream.end();
});
writeStream.on('finish', resolve);
writeStream.on('error', reject);
});
fileWrites.push(promise);
});
busboy.on('finish', async () => {
await Promise.all(fileWrites);
for (const file in uploads) {
const filepath = uploads[file];
console.log(`Finish filename:${filepath}`);
const [textDetections] = await visionClient.textDetection(filepath);
const [annotation]:any = textDetections.textAnnotations;
const text = annotation ? annotation.description : '';
console.log("## OCR ## " + text);
response.write(text);
try {
console.log(`UN LINK FILE : ${filepath}`);
fs.unlinkSync(filepath);
} catch(e) {
}
}
response.send();
});
busboy.on('error', async () => {
console.log("busboy on error");
});
busboy.end(request.rawBody);
});
2.呼び出しページ
<!DOCTYPE html> <html lang=“ja”> <head> <meta charset=“uft–8”> </head> <body> <h1>Cloud Vision</h1> <form action=“https://{Firebase Cloud Functions のURL}/helloVisions” method=“POST” enctype=“multipart/form-data”> <p><input type=“file” name=“upload_file1”></p> <p><input type=“submit” value=“Submit”></p> </form> <h1>Log Console</h1> <a href=“https://console.cloud.google.com/logs” target=“_blnak”>GCP Log Console</a> </body> </html>
3.実行
OCRにかけるサンプル画像
ソースコードを適当にキャプチャ。
アップロードページ
上記アップロードページを起動
上記アップロードの、Cloud Vision にサンプルファイルを指定し、submit
結果が返ってきた!OK! ながかった。
GCPのログ
ログにも、想定した結果がはきだされている。
4.tscエラー対応
最終的には、以下の対応を行う必要はなかった(冒頭の記述参照)のだが、メモ。
(1) Cannot find name ‘AsyncIterator’
https://github.com/apollographql/graphql-subscriptions/issues/83
tsconfig.json
“compilerOptions”: { “lib”: [ “esnext.asynciterable” ],
(2) require(…) is not a function
https://stackoverflow.com/questions/33007878/nodejs-typeerror-require-is-not-a-function
5.参考
- Node.js
- Firebase admin SDK
- Samples