pdffontsの自動実行(1)
昨日の朝、白鳥の群れが飛んでいくのを見ました。春ですね。さて、今週はfetchで取得したpdfファイルに対して、pdffontsを実行する部分を実装したいと思います。
対象ファイルのpdffontsの結果を取得
kintoneのRestAPIで処理対象のレコードを複数取得して、各レコードに添付されているpdfのpdffontsの実行結果を対象レコードに反映するとなると、こんな感じの実装ができれば良いかなと思いました。
ダウンロードしたpdfのpdffontsの実行
// pdffontsの実行 とコメントを書いたら、GitHub Copilotが以下のコードを提案してくれたので中身を確認します。
// pdffontsの実行
runPdffonts = async (resultPath: string, pdfFile: string): Promise<boolean> => {
const exec = require("child_process").exec;
const command = `pdffonts ${pdfFile} > ${resultPath}`;
const promise = new Promise<boolean>((resolve, reject) => {
exec(command, (error: any) => {
if (error) {
reject(error);
}
resolve(true);
});
});
return promise;
}
子プロセスとしてpdffontsを実行して、その結果を対象pdfファイル名に対応したテキストファイルに書き出す関数です。戻り値は正常に実行できた場合にresolve(true)を、エラーの場合はreject(error)をpromiseとして返却します。pdffontsの実行成否で結果を返却するのはイメージできるのですが、rejectとresolveが何を意味するのかを確認します。
コールバック関数のresolveとreject
Promise インスタンスの作成は new Promise(executor) が基本形で、executorはコールバック関数です。executorは引数としてresolve, rejectの2つを受け取ります。上記のコードを見ると、pdffontsの実行成否によりresolveとrejectを受け取っています。「Promise コンストラクタと Executor 関数」を読んでみると、実行に成功した場合にはPromise履行時の値としてtrueを、エラーが発生した場合はPromise拒否時の理由としてエラーを返却するということがわかります。コメントを書くだけで、正しい関数が生成されたわけですね。提案されたコードが正しいかどうか判断できるなら、コーディングの効率が上がりそうです。(私の場合は、使いこなすために、まだまだ精進しなければなりません)関数runPdffontsの結果がtrueだった場合は、kintoneのレコードのpdffonts実行結果カラムにテキストに出力された内容を反映し、レコードのステータスをpdffonts実行済に進めれば良さそうです。エラーの場合は、レコードのpdffonts実行結果カラムもレコードのステータスも変更しないことにします。
子プロセスとしてpdffonts実行を試してみる
正しそうなので、ここまでの実装内容を試してみます。
TypeError: Promise resolver undefined is not a function
at new Promise (<anonymous>)
at runPfdfonts (/home/tetrapod/fetchAPItest/fetchPDF/fetchPDF.js:96:21)
at /home/tetrapod/fetchAPItest/fetchPDF/fetchPDF.js:48:37
at Array.map (<anonymous>)
at /home/tetrapod/fetchAPItest/fetchPDF/fetchPDF.js:42:24
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v18.13.0
あー、resolveとrejectにあたるコールバック関数が無いというエラーが。履行時の関数と、拒否時の関数をthenに記述します。
// pdffontsの実行
runPdffonts = async (resultPath: string, pdfFile: string): Promise<boolean> => {
const exec = require("child_process").exec;
const command = `pdffonts ${pdfFile} > ${resultPath}`;
const promise = new Promise<boolean>((resolve, reject) => {
exec(command, (error: any) => {
if (error) {
reject(error);
}
resolve(true);
});
});
return promise;
}).then(
function resolve(val) {
console.log(`onFulfilled ${val}`);
},
function reject(reason) {
console.log(`onRejected reason ${reason}`);
} );
return promise;
}
改めて、実行してみます。
$ node fetchPDF.js
SET TOKEN
getRecords 3 records
fetch start README.md contentType:application/octet-stream
fetch start manifest.json contentType:application/json
fetch start Chrome拡張MV3について.pdf contentType:application/pdf
onRejected reason Error: Command failed: pdffonts temp/manifest.json > temp/manifest.json
Syntax Error: Document stream is empty
onRejected reason Error: Command failed: pdffonts temp/README.md > temp/README.md
Syntax Error: Document stream is empty
onFulfilled true
ファイル書き出しOK=>manifest.json
ファイル書き出しOK=>README.md
ファイル書き出しOK=>Chrome拡張MV3について.pdf
今度は、成功しました。エラーも発生していますが、こちらはpdfファイルでないファイルに対してpdffontsを実行したため発生したエラーです。念の為pdfファイル以外が添付されていた場合には、処理対象外にするようにしたほうが良さそうです。
pdffontsの実行結果のファイルも確認します。
今日のまとめ
対象レコードから取得した添付ファイルの情報をもとに、添付ファイルをダウンロードして、pdffontsの実行結果をファイルに保存するところまで確認できました。ここまでの実装で、非同期処理のPromiseについても理解が進んだ気がします。しかし、まだ理解が浅いので、引き続き学習を続けます。