2024年9月22日

GASを使ってGoogleドライブファイルの一括ダウンロード(改良版)

宮島弥山山頂から
宮島弥山山頂から



さて,前々回,GoogleドライブからローカルPCへ一括ダウンロードを実証しましたが,全てのダウンロードファイルをBase64エンコードで変換して,GASからHTMLに引き渡すテーブル(二次元配列files)に格納しました。

そのため,テーブル自体が大きくなりすぎ,ブラウザにおいてエラーが発生する恐れがありました。


GASを使って複数ファイルの一括ダウンロード実証

GASを使って複数ファイルの一括ダウンロード実証

GASを使ってGoogleドライブ内にある複数のファイルを一括でダウンロードする方法を実証した


そこで,今回はダウンロードファイル毎に,GASで「ファイル本体Data」を作成しHTMLに引き渡すように改良しました。

HTMLとGASとのやり取りは多くなりますが,GASからHTMLに渡すテーブル(二次元配列files)は小さくなり,ブラウザの容量オーバーなどの危険性は回避できると思います。

それでは学習を始めましょう。





  今回のGASスクリプトのあらまし


今回作成するスクリプトの外観図は前々回とほぼ同様です。

GASダウンロード外観図
GASダウンロード外観図


今回は⑧のモーダルダイアログ内で,ダウンロードファイル毎にGASから本体情報を取得するように改良しました。



  シーケンスフローについて


今回のシーケンスフローは以下のとおりです。

①GASスクリプト起動(前々回と同じ)
スプレッドシートにダウンロード対象ファイルを表示するGASスクリプトを起動します。

②スプレッドシート表示(前々回と同じ)
ファイル検索ボタンを押すことで,指定フォルダからダウンロード対象ファイルを抽出してスプレッドシート上に表示します。

スプレッドシートのイメージは,以下のとおり同じです。


スプレッドシートのイメージ図
スプレッドシートのイメージ図



③ダウンロードファイル選択(前々回と同じ)
ダウンロードしたいファイルをチェックします。

④ダウンロード実行GASスクリプト起動(前々回と同じ)
ダウンロード実行ボタンを押すことで,ダウンロードファイルの存在を確認するGASスクリプトを起動します。

⑤モーダルダイアログの表示(前々回と同じ)
ダウンロードするファイルがあれば,簡易ブラウザであるモーダルダイアログを表示します。

モーダルダイアログのイメージは次のとおり前々回と同じです。

モーダルダイアログ図
モーダルダイアログ図


⑥ダウンロード開始(前々回と同じ)
モーダルダイアログのダウンロード開始ボタンを押下し,「google.script.run」関数 を使ってダウンロードファイル情報を収集するスクリプトを起動します。


⑦ダウンロード情報の取得(前々回と同じ)
「google.script.run」関数 のリターン値からダウンロードファイル情報群(2次配列)を取得します。

ダウンロード本体の取得とダウンロードの実行(改良)および後処理(前々回に同じ)

ダウンロードファイル情報群(2次配列)から,1ファイル(各行)ずつgoogle.script.run.関数 を使ってファイル本体を読込み,HTML側のa要素を使ってローカルPCにダウンロードするよう改良します。

最後に,ダウンロード終了時にgoogle.script.run.関数 を使ってGASスクリプトを呼びだし,スプレッドシート上のダウンロードファイル指定を解除します。(前々回と同じ)

前々回と同じところは,コーディングのみ記載します。




  スプレッドシートを表示するGASスクリプト

まずは,スプレッドシートを表示するGASスクリプト(①②)のコーディングです。

main.gs

function main() {
  //スプレッドシートから検索するフォルダーIDと検索するMIMETYPEを読み込む
  //Activeシートオブジェクト指定
  var sheet = SpreadsheetApp.getActiveSheet();        //スプレッドシートクラス指定
 
  //スプレッドシートからフォルダーIDを読み込む
  var folder_id = sheet.getRange(2,3).getValue();
  console.log(folder_id);
  
  //エラーメッセージ欄をクリアする。
  sheet.getRange(2,4).setValue(" ");                  
  sheet.getRange(4,4).setValue(" ");

  //フォルダーIDからフォルダーオブジェクトを取得する
  var folder = getfolderobj(folder_id);
  if(folder == "err"){
    sheet.getRange(2,4).setValue("フォルダーIDがありません。再度実行して下さい。");
    console.log("フォルダーIDがありません。");           //エラー表示
    return;
  }
  
  console.log(folder.getName()); //現在のドライブ
  console.log(folder.getId());   //現在のドライブ
  
  //スプレッドシートからMIMEタイプを読み込む
  var mime_type =  sheet.getRange(4,3).getValue();  
  if (mime_type == ""){
    sheet.getRange(4,4).setValue("MIMEタイプがありません。指定して下さい。");
    console.log("MIMEタイプがありません。");   //エラー表示      
    return;    
  }
  //MIMEタイプを配列に分解する
  var mime_types = mime_type.split(/,/g);
  console.log(mime_types.length);
 
  //検索条件文字列を初期化する
  var serch_cond = "";
  //MIME_TYPEから検索条件文字列を編集する。
  for(let i=0; i < mime_types.length; i++){
    switch(mime_types[i]){
      case "text/plain":
      case "text/csv":
      case "text/html":
      case "application/pdf":
      case "image/jpeg":
      case "image/png":
        if ( i == 0 ){
          serch_cond = serch_cond + 'mimeType = "' + mime_types[i] + '"';
        }
        else{
          serch_cond = serch_cond + ' or mimeType = "' + mime_types[i] + '"';
        }
        continue;
      default:
        sheet.getRange(4,4).setValue("指定できないMIMEタイプがあります。ご確認下さい。");
        console.log("使えないMIMEタイプがあります。");   //エラー表示      
        return;
    }
  }
    
  console.log("serch_cond=" + serch_cond);
 
  let row_count = sheet.getLastRow() -7;    //HEADER7行分を除く
  let col_count = sheet.getLastColumn() -1; //利用カラムの最大数

  if(row_count > 0){
    //8行目から最終行までをクリア
    sheet.getRange(8, 1, row_count, col_count+1).clear({contentsOnly: true, skipFilteredRows: true});
  }
 
  let row = 8;
  let counter = 1;

  //該当フォルダー下の該当するファイルのコレクションを取得する
  const files = folder.searchFiles(serch_cond);
  //console.log(files);
  while (files.hasNext()){              //コレクションファイルが存在する間繰り返す
    var file =files.next();             //コレクションファイルを読む
    console.log(file.getName());        //ファイル名を出力する
      
    //ファイル名,ファイルID,ファイルURLを編集する
    let col = 2;
    while (col < 7){                    //配列の数だけ繰り返す
      sheet.getRange(row,col).setBorder(true,true,true,true,true,true); //セル上下左右罫線書く
      sheet.getRange(row,col).setVerticalAlignment('middle');   //セル垂直中央に設定
      switch(col){
        case 2:  //項番を編集する
          sheet.getRange(row,col).setHorizontalAlignment('center'); //セル水平中央に設定
          sheet.getRange(row,col).setValue(counter);
          break;
        case 3:  //ファイル名を編集する
          sheet.getRange(row,col).setValue(file.getName());
          break;
        case 4:  //ファイルIDを編集する
          sheet.getRange(row,col).setWrap(true);             //セル内折り返し設定
          sheet.getRange(row,col).setValue(file.getId());    //ファイルIDを編集する
          break;
        case 5:  //ファイルURLを編集する
          sheet.getRange(row,col).setWrap(true);             //セル内折り返し設定
          sheet.getRange(row,col).setValue(file.getDownloadUrl()); //ファイルDWLURLを編集する
          break;
        case 6:
          sheet.getRange(row,col).setHorizontalAlignment('center'); //セル水平中央に設定
          sheet.getRange(row,col).insertCheckboxes();
      }
      col++;
    }
    row++;
    counter++;
  }
  SpreadsheetApp.flush();
  return;
}

function getfolderobj(folder_id) {
  try {
    return DriveApp.getFolderById(folder_id);
  }
  catch(e) {
    return "err";
  }
}



  ダウンロードファイルの存在確認とモーダルダイアログの表示

次に,スプレッドシートのダウンロード実行ボタンを押下することでダウンロードファイルの存在確認とモーダルダイアログを表示します。


dwl_make.gs

function dwl_perform(){
  // ダウンロードファイルの存在を確認する
  var files = dwl_exist();
  
  if(files[0][0] == ""){
    Browser.msgBox("ダウンロードファイルが指定されていません");
    return;
  }
  
  //htmlを起動する。
  var output = HtmlService.createHtmlOutputFromFile("index");
  SpreadsheetApp.getUi().showModalDialog(output, 'ダウンロードファイル指示');
}

function dwl_exist(){
  //データ配列の初期化
  var files = [["","","",""]];
  
  //Activeシートオブジェクト指定
  var sheet = SpreadsheetApp.getActiveSheet(); //スプレッドシートクラス指定
  
  //ダウンロードファイルが指示されているか,確認する。
  let row_count = sheet.getLastRow() -7;    //HEADER7行分を除く
  let col_count = sheet.getLastColumn() -1; //利用カラムの最大数

  if(row_count < 1){
    Browser.msgBox("ダウンロードすべきファイルがありません,再度検索実行後指示して下さい");
    return;
  }
  //ダウンロード指示されたファイルを探す
  let i = 0;
  var dwl_filename = "";
  var dwl_fileid   = "";
  var dwl_fileurl  = "";
  let row = 8;
  //ダウンロードファイル名,ダウンロードURL,zダウンロードIDを取得する
  while(row < (row_count + 8)){ 
    if(sheet.getRange(row, 6).getValue() == true){     //ダウンロード指示有を確認
      dwl_filename = sheet.getRange(row,3).getValue(); //ファイル名を退避
      dwl_fileid = sheet.getRange(row,4).getValue();   //ファイルIDを退避
      dwl_fileurl = sheet.getRange(row,5).getValue();  //ファイルURLを退避      
      files[i] = [dwl_filename, dwl_fileid, dwl_fileurl, row];
      i = i + 1;
    }  
    row = row + 1;
  }
  return files;
}


次に,モーダルダイアログを表示するコードを作ります。

index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <style>
     #drop {
      height: 50px;
      border: #D4D4D4;
      border-style: dashed;
      border-width: 3px;
      padding: 10px;
      text-align: center;
      } 
     #click {
      text-align: right;
     }
     #result {
      text-align: center;
     }    
    </style>
  </head>
  <body>
    <div>
      <div id="drop">
         <p>チェックしたファイルをダウンロードします
          <button id="Dwl_button" type="button">ダウンロード開始</button>
        </p>
      </div>
      <hr />
      <div id="click">
        <button id="click_1" type="button" >実行</button>
        <button id="click_2" type="button" >終了</button>
      </div>
      <div>
        <pre id="result"></pre>
      </div>
    </div>
  </body>
</html>




  ダウンロードファイル情報の取得

モーダルダイアログの「ダウンロード開始ボタン」を押下することで,ダウンロードファイル情報(二次元配列files)を取得するjavascriptを作ります。

以下のコードをindex.html内の</html>前に挿入します。


index.html

<script>
  //退避エリアを初期値する
  var dwl_files = [["","","",""]];
  window.addEventListener('DOMContentLoaded', function() {
    document.getElementById("Dwl_button").addEventListener('click', function(e){
      //引数エリアを初期化する。
      var files = [["","","",""]];      
      //ダウンロードファイルの情報を取得する
      google.script.run
      .withSuccessHandler(function(files){
        
        dwl_files = files; //引数エリアを退避する。
        //alert("files= " + files[0][0]);
           
        if(files[0][0] === ""){
          var msg = "日付=" + getToday() + "  ダウンロードファイルはありません。";
          msg = msg + "\n\n" + "終了ボタンを押して下さい。";
        } else {
          var msg = "日付=" + getToday() + "  ファイル名=" + files[0][0]; 
          msg = msg + "\n\n" + "ダウンロードします。実行ボタンを押して下さい"; 
        }
        document.getElementById('result').textContent = msg;
      })
      .withFailureHandler(function(err) {  //ファイル情報取得に失敗した場合の処理
        alert("ダウンロードが失敗しました。\n\nエラーメッセージ=" + err.message);
      }).dwl_exist();
    },true);

    //*** 次の関数をココに入れる。 ***
  });
  
  function getToday(){
    let today = new Date();
    today.setDate(today.getDate());
    const yyyy = today.getFullYear();
    const mm = ("0"+(today.getMonth()+1)).slice(-2);
    const dd = ("0"+today.getDate()).slice(-2);
    const result = yyyy+'-'+mm+'-'+dd;
    return result;
  }

  function spread_syuryo(){
    // スプレッドシートで、アップロードUIを自動的に閉じる
    google.script.host.close();
  }
</script>




  ダウンロード本体の取得とダウンロードの実行(改良)および後処理

基本は前々回と同じですが,ダウンロードの実行(HTML側)の繰り返し処理の中に,ダウンロードファイル本体の取得処理(GASのdwlfile_syori(files[i])関数の呼び出し)を入れています。

以下に,HTMLのjavascriptのコードを記載します。このコードは,前述の「document.getElementById("Dwl_button").addEventListener('click', function(e)」の次の関数として設定します。


index.html

    document.getElementById("click_1").addEventListener('click', async function(e){
      // 退避エリアから引数を戻す
      files = dwl_files;
      
      if(files[0][0] === ""){
        alert("ダウンロードファイルがありません。ダウンロード開始ボタンを押して下さい。")
        return;
      }
      const sleep = ms => new Promise(resolve => setTimeout(resolve, ms || 1000));
      for( let i = 0; i < files.length; i++){
        var msg = "日付=" + getToday() + "  ファイル名=" + files[i][0]; 
        msg = msg + "\n\n" + "ダウンロードします。実行ボタンを押して下さい"; 
        document.getElementById('result').textContent = msg;
        
        google.script.run
        .withSuccessHandler( function(data){
          let a = document.createElement("a");
          document.body.appendChild(a);
          a.download = files[i][0];
          a.href = data;
          a.click();
        })
        .withFailureHandler(function(err) {  //ファイル情報取得に失敗した場合の処理
          alert("ダウンロード終了処理が失敗しました。\n\nエラーメッセージ=" + err.message);
        }).dwlfile_syori(files[i]);
        
        var ms = 3000;
        await sleep(ms);
      }

      google.script.run
      .withSuccessHandler(function(files){
          
        dwl_files = files; //引数エリアを退避する。
          
        var msg = "日付=" + getToday() + "  ダウンロードは終了しました。";
        msg = msg + "\n\n" + "終了ボタンを押して下さい。";
        document.getElementById('result').textContent = msg;
      })
      .withFailureHandler(function(err) {  //ファイル情報取得に失敗した場合の処理
        alert("ダウンロード終了処理が失敗しました。\n\nエラーメッセージ=" + err.message);
      }).dwlfile_syuryo(files);
    },true);

    document.getElementById("click_2").addEventListener('click', function(e){
      //alert("終了ボタンが押されました");
      spread_syuryo() 
    },true);

【補足】

GASより受け取った二次元配列「files」を利用して,ファイルの繰り返し処理の中でファイル本体のデータを取得する関数「dwlfile_syori(files[i])」をgoogle.script.run関数で呼び出しています。

ファイル本体のBase64エンコードである「DataURL(data)」が正常に返された場合,「body要素にa要素を追加し,「DataURL(data)」のdownloadを行います。

その他は,前々回と同様です。


今回は,「google.script.run」関数を使って,ファイル本体「data」を取得するGASの処理関数「dwlfile_syori(file)」コードを作成します。


dwl_make.gs

function dwlfile_syori(file){
  //ファイルIDからファイルオブジェクトを取得する
  file_obj = DriveApp.getFileById(file[1]);

  //ファイルブジェクトのBlobオブジェクトを取得する
  blob_obj = file_obj.getBlob();
  console.log(blob_obj.getBytes());
  console.log(blob_obj.getContentType());

  //Blobオブジュクトからバイトに変換しDataURLを編集する
  var data = `data:${blob_obj.getContentType()};base64,${Utilities.base64Encode(blob_obj.getBytes())}`; 

  return data;
}



最後に,「google.script.run」関数で呼ばれるGASの後処理関数「dwlfile_syuryo(files)」のコードを以下に示します。

dwl_make.gs

function dwlfile_syuryo(files){
  
  //Activeシートオブジェクト指定
  var sheet = SpreadsheetApp.getActiveSheet(); //スプレッドシートクラス指定
  
  console.log("ファイル名 = " + files[0][0]);
  console.log("ファイルID = " + files[0][1]);
  console.log("ファイルURL= " + files[0][2]);
  console.log("row= " + files[0][3]);
  console.log("ファイルDate =" + files[0][4]);

  //ダウンロード処理済みのファイルがある時は,チェックを消す。
  for(let i = 0; i < files.length; i++){
    if(files != undefined && files[i][0] != ""){
      sheet.getRange(files[i][3],6,1,1).clear({contentsOnly: true, skipFilteredRows: true});
      sheet.getRange(files[i][3],7,1,1).clear({contentsOnly: true, skipFilteredRows: true});
      sheet.getRange(files[i][3],6,1,1).setHorizontalAlignment('center'); //セル水平中央に設定
      sheet.getRange(files[i][3],6,1,1).insertCheckboxes();
    }
  }
  files = [["","","",""]]; //filesを初期化する。
  return files;
}

【補足】

この関数では,スプレッドシート上のダウンロードファイル指定をクリアしています。


  まとめ

今回は,前々回の「複数ファイルの一括ダウンロードの実証」について,ダウンロード毎にファイル本体のBase64エンコード「DataURL」を取得して行う方法を学習しました。

これで,GASとHTMLの連絡ファイルテーブル(二次元配列files)が異常に大きくならないと思います。

GASとHTMLは「google.script.run関数」で簡単に連係できるので便利ですね。

少し,長くなりましたが,以上です。

それでは,楽しいITリテラシーライフをお過ごしください。


(ご注意)情報の正確性を期していますが,実施される場合には自己責任でお願いします。


0 件のコメント: