Google Apps Scriptでドキュメントを一括でPDFにする【GAS】

現在受託したアプリの作成の過程で、Documentファイルを一括でPDFに変換し、結合し1枚にするものを作成中です。その過程で作成したスクリプトを公開します。以前まとめた、PDFの作成のエントリーの結合スクリプトも利用しています。また、実験としてgetAsで生成するのとUrlfetchApp.fetchAllでリクエストを投げて生成するのとではどちらが早いのか?の実験もしています。

Google Apps ScriptでPDFを作成する【GAS】

今回利用するスプレッドシート等

スクリプトの中のfolderidという変数に、ドキュメントが入ってるフォルダのIDを指定する必要があります。量が多い場合、かなりの時間が掛かるので、6分のタイムアウトを突破するテクニックの併用も必要です。

結合で利用してるmergePdfs関数は上記のStackOverFlowで公開されてるスクリプトを利用しています。

Google Apps Scriptで6分の壁(タイムアウト)を突破する - 番外編【GAS】

検証結果

7個のドキュメントでPDFへの変換をする形で計測。結果的には

  • getAs :  8.5秒
  • UrlfetchApp.fetchAll : 16.5秒

やはりHTTPリクエストはオーバーヘッドが掛かるのか、倍の差でgetAsで変換するほうが全然早かったです。

ソースコード

引数のarrayはファイル名、ファイルIDの2つが入ってる二次元配列となっています。[["filename","fileid"],["filename","fileid"],["filename","fileid"]]といった形になっています。

Drive API v2等でバッチ処理を使ったらもっと早く生成できるのかもしれませんが、今回は利用していません。

getAsを利用する場合

//getAsでPDF変換を実行(urlfetchapp.fetchallより倍以上早い)
async function batchman3(array,folderid){
  //現在時刻を取得する
  var starttime = new Date();

  //保存場所を指定
  let folder = DriveApp.getFolderById(folderid);
  
  //arrayからURLを生成
  let pdflist = [];
  
  for(let i = 0;i<array.length;i++){
    //fileid
    let fileid = array[i][1];
    let filename = array[i][0] + ".pdf";

    //getAsでPDF変換
    let docs = DocumentApp.openById(fileid);
    let pdf = docs.getAs('application/pdf');

    //ドライブにPDFを生成する
    let pdfid = folder.createFile(pdf).setName(filename).getId();

    //配列に追加する
    pdflist.push(pdfid);
  }

  //完了時刻との差を取る
  let endtime = new Date();
  let diff = (endtime.getTime()-starttime.getTime()) / 1000;
  console.log(diff + "秒")

  return pdflist;
}
  • 単純にarrayの中のfileidを元にgetAsにてPDFに変換してるだけのスクリプトです。
  • 変換後のファイルをDriveに生成し、ファイルのIDを取得しています。

UrlfetchApp.fetchAllを利用する場合

//バッチ処理でPDF変換を実行(Drive API v3を利用)
async function batchman2(array,folderid){
  //現在時刻を取得する
  var starttime = new Date();
  
  //arrayからURLを生成
  var arrayman = [];

  //Access Tokenを取得
  var token = ScriptApp.getOAuthToken();

  for(let i = 0;i<array.length;i++){
    //URLを構築
    let url = "https://docs.google.com/document/d/" + array[i][1] + "/export?format=pdf";

    //リクエストボディを作成
    let req = {
        "url" : url,
        "method" : "GET",
        'headers': {"Authorization": " Bearer " + token}
    }

    //配列に追加
    arrayman.push(req);
  }

  //fetchAllで一度にリクエスト
  var response = UrlFetchApp.fetchAll(arrayman);

  //保存場所を指定
  let folder = DriveApp.getFolderById(folderid);

  //responseからPDFを生成する
  let pdflist = []
  for(let j = 0;j<response.length;j++){
    //Blobを取得
    let blob = response[j].getBlob();

    //ファイルを生成(シート名でファイル名をセット)
    let pdfid = folder.createFile(blob).setName(array[j][0] + ".pdf").getId();

    //生成したPDFのIDを配列に加える
    pdflist.push(pdfid)
  }

  //完了時刻との差を取る
  let endtime = new Date();
  let diff = (endtime.getTime()-starttime.getTime()) / 1000;
  console.log(diff)

  return pdflist;
}
  • GETによるPDF作成のURLを生成して、まとめてUrlfetchApp.fetchAllに投げています。
  • その後、レスポンスデータからDriveに対してファイル名を付けて生成する仕組みです。

結合するスクリプト

変換した結果を配列にファイルのIDとして格納し、これを元にPDF結合メソッドにて1枚のファイルにします。およそ1ページ7個で152kbのファイルサイズなので、100枚でも2.1MB程度のファイルサイズになります。気をつけなければならないのは、6分の壁があるので、この生成に於いての上限は6分で生成できるサイズとなるので注意が必要です(途中で辞める事ができないので、分割して6分の壁を超えるといったことができません)

//指定フォルダ内のPDFを結合する
function pdf_merge(folderid) {
  //UIを取得
  let ui = DocumentApp.getUi();

  //スピナーを表示
  var html = HtmlService.createHtmlOutputFromFile('spinner').setWidth(300).setHeight(150);
  ui.showModalDialog(html, 'PDF結合中');

  //フォルダを取得する
  let folder = DriveApp.getFolderById(folderid);
  
  //フォルダ内のPDFをリスト化
  let pdflist = folder.getFiles();

  //リスト用の配列
  let mergearr = [];
  
  //拡張子の指定(正規表現)
  let pattern = /.*\.pdf$/;
  
  //結合後ファイル名の指定
  let filename = "結合後のPDF.pdf"
  
  //mergearrにPDFデータをpush
  while (pdflist.hasNext()){
    //ファイルを取得
    var file = pdflist.next();

    //拡張子をチェック
    if (pattern.test(file.getName())){
      mergearr.push(file);
    }
  }

  //PDF結合実行
  let mergedFile = mergePdfs(folder, filename, mergearr);

  //結合PDFのIDを取得
  let mergeid = mergedFile.getId();
  let prop = PropertiesService.getScriptProperties();
  prop.setProperty("pdfid",mergeid);

  //終了メッセージ表示
  var html = HtmlService.createHtmlOutputFromFile('result').setWidth(400).setHeight(250);
  ui.showModalDialog(html, '出力結果');
}
  • 指定フォルダ内のファイルを探索し一覧にし、その中で拡張子がPDFのものだけをピックアップしています。
  • 配列にblobとして各PDFファイルを格納します。
  • 格納したものを、meregePdfsメソッドを利用して1枚に結合。mergedFileにはそのファイルのID等が返ってくる。
  • 結合に掛かった時間ですが、1ファイル1ページもので33kbで1秒あたり1.07秒。10枚で330kbとし生成時間は11秒の計測時間が出ました。100枚ならば3.3MB、時間にして110秒(1分50秒)程度なので、6分の限界を考えると結合で利用できる枚数は、最大でも500枚程度までということになるかと思います(ページ数が増えたり画像の有無でその上限はずっと小さくなると思います)。
  • PDFを生成⇒ダウンロード、ローカル環境でpdfasなどを利用して結合のほうが現実的かなぁと思います。

関連リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)