PR

GoogleAppsScriptの始め方(Googleドライブの扱い方)

GoogleAppsScriptで、Googleドライブのファイルやフォルダを扱う場合のGASでの扱い方について解説をします。「GoogleAppsScriptの使い方(基本的な使い方)」では、スプレッドシートに紐づいたコンテナバインドスクリプトを中心に解説をしました。が、今回は、Googleドライブに対して操作を行う、ドライブ上で独立して動作するスタンドアロンスクリプトについて解説をします。実務で良くあるフォルダやファイルの整理を自動化のスクリプトを作成しながら、Googleドライブに対する操作をご紹介します。

スポンサーリンク

Googleドライブの扱い方

GASで、Googleドライブのファイルやフォルダを扱う場合は、DriveAppクラスを使います。DriveAppクラスの各種メソッドを使って各種操作を行います。
以下のようなGoogleドライブの構成を例として、フォルダの取得、ファイルの取得のメソッドをご紹介します。

フォルダ・ファイルの構成(Googleドライブ)

フォルダの取得

まず、マイドライブの中の「業務フォルダ」を取得してみます。

フォルダの取得方法

GASでGoogleドライブのフォルダを取得する場合は、DriveAppのgetFolderByIdメソッドを使います。フォルダIDであればGoogleドライブでそのフォルダを開いている際のURLの以下の部分です。

フォルダID(GAS)

DriveAppクラスからフォルダを取得する。

フォルダの取得方法(GAS)
function myFunction() {
  const folder = DriveApp.getFolderById("フォルダID");
  console.log(folder.getName());
}

スクリプトで使用されているgetNameメソッドはフォルダ名を取得するメソッドです。

フォルダ名の取得(GAS)

ファイルの取得

次に、「業務フォルダ」内に保存されていたファイル「シートの取得用」を取得してみます。

ファイルの取得方法

Googleドライブのファイルを取得する場合は、DriveAppのgetFileByIdメソッドを使います。

ファイルの取得(GAS)
function myFunction() {
  const file = DriveApp.getFileById("ファイルID");
  console.log(file.getName());
}

※ファイルIDは、以下の通り

全てのファイルを取得する。

const files = DriveApp.getFiles();」が、Googleドライブのすべてのファイルを取得するスクリプトになります。取得したデータは「FileIterator(ファイルイテレータ)」という形式です。イテレータを使えばデータの集まりに対して、「次」、「次」と抽象的に命令するだけで、順番に各データを取得することができます。

全てのファイルの取得(GAS)
function myFunction() {
  const files = DriveApp.getFiles();
  while (files.hasNext()) {
    const file = files.next();
    console.log(file.getName());    
  }
}

特定のフォルダーの中のファイルを全て取得する。

特定フォルダのファイルの取得(GAS)
const gyomuFolder = DriveApp.getFolderById("フォルダID");

function myFunction() {
  const files = gyomuFolder.getFiles();
  while (files.hasNext()) {
    const file = files.next();
    console.log(file.getName());    
  }
}
スポンサーリンク

ファイル整理の自動化

それでは、実際に実務で良くある「フォルダの整理」「ファイルの整理」をスクリプトを作成して自動化してみます。

ファイル整理自動化の内容

「ドキュメント」フォルダ内のファイルの内、ファイルの最終更新日が1ヶ月以上前のファイルについては、「old」フォルダ内に新しく「年月」のフォルダを作成して移動させます。
※わかりやすいようにファイル名は、最終更新日としています。

ファイル整理自動化のイメージ(GAS)
[ スクリプト実行前の「ドキュメント」フォルダ ]
スクリプト実行前の「ドキュメント」フォルダ
[ スクリプト実行後 の「ドキュメント」フォルダ]
スクリプト実行後の「ドキュメント」フォルダ

ファイル整理自動化の準備

作成する関数(スクリプト)

seiriFiles関数

「ドキュメント」フォルダ内のファイルを調べて、最終更新日が1ヶ月以上前のファイルを取得します。

sakuseiFolder関数

seiriFiles関数から呼び出され、移動対象となるファイルを取得して「old」フォルダ内に「年月」フォルダを作成します。

idouFiles関数

seiriFiles関数から呼び出され、sakuseiFolder関数で作成した「年月」フォルダに移動対象のファイルを移動させます。

ライブラリーの導入

このスクリプトでは、日付データを扱いますので「dayjs」というライブラリーを導入します。
dayjsのスクリプトIDは、

1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB

自動化スクリプトの作成

それでは、スクリプトを作っていきます。

スクリプトエディターを開く

今回は、Googleドライブ上で独立して動作するスタンドアロンスクリプトを作成します。

スクリプトエディターの起動(GAS)

seiriFiles関数の実装

「ドキュメント」フォルダ内のファイルを調べて、最終更新日が1ヶ月以上前のファイルを取得します。プロジェクト名は、「ファイルの整理」、ファイル名はコード.gs、関数名はseiriFilesとします。

seiriFiles関数の実装1(GAS)
フォルダ内のファイルの取得

getFiolderByIdメソッドで「ドキュメント」フォルダを取得してgetFilesメソッドで、「ドキュメント」フォルダ内のファイルを全て取得してfilesに格納します。

seiriFiles関数の実装2(GAS)
function seiriFiles() {
  const documentFolder = DriveApp.getFolderById("フォルダID");
  const files = documentFolder.getFiles();

3行目の「getFiles();」が、「ドキュメント」フォルダ内の全てのファイルを取得するスクリプトになります。取得したデータは「FileIterator(ファイルイテレータ)」という形式です。イテレータを使えばデータの集まりに対して「次」「次」と抽象的に命令するだけで、順番に各データを取得することができます。

[ 全てのファイルが取得されているかを確認 ]

filesにファイルが格納されているか確認してみます。

seiriFiles関数の実装3(GAS)

[確認の為のスクリプトの解説]

「while (files.hasNext()) {」は、filesに次のデータがある限り処理を続けます。
「const file = files.next();」で、files(「ドキュメント」フォルダにあるファイル)の次のデータを取得しています。
「Logger.log(file.getName());」が「const file = files.next();」で取得したデータに対する処理になります。今回は取得したデータの名前をログに表示させています。

1ヶ月前の日付の取得

現在の日付から1ヶ月前の日付を取得します。

seiriFiles関数の実装4(GAS)

dateオブジェクト.setMonth(引数)で、dateオブジェクトの月から1ヶ月前の日付をセットします。

留意:
taisyoFiles = [ ];は移動対象のファイルオブジェクトを格納する配列で、
folderNames=[ ];は、作成するフォルダ名を格納する配列となります。

[ hitothukimaeの内容を確認します。]

seiriFiles関数の実装5(GAS)

現在日付が2023/07/07ですので、1ヶ月前の2023/06/07となっています。

注意:
formatは、”yyyy/MM/DD”ではなく、”yyyy/MM/dd”となりますので、ご注意下さい。”yyyy/MM/DD”だと、以下のように158と変な数値となってしまいます。

最終更新日のチェックと対象ファイルの格納

次に、whileとhasNextメソッド、nextメソッドで「ドキュメント」フォルダから取得したファイルの最終更新日が1ヶ月以上前か否かをチェックします。
※ファイルの最終更新日を取得するには、getLastUpdated()メソッドを使用します。

seiriFiles関数の実装7(GAS)

最終更新日(lastUpdated.getTime())の方が、現在日付の1ヶ月前の日付(hitothukimae.getTime())より前の日付だった場合は配列(taisyoFiles)にファイルを格納します。そして最終更新日からYYYYMM形式の文字列を生成してfolderNamesに格納します。
※getTime() ではDate型の日付をミリ秒に変換した数値を返却してくれます。

[ taisyoFilesの確認 ]

taisyoFilesの配列に移動対象のファイルが格納されているかを確認します。

seiriFiles関数の検証1(GAS)
[ folderNamesの確認 ]

folderNamesの配列に移動対象のファイルが格納されているかを確認します。

seiriFiles関数の検証2(GAS)
関数の呼び出し

sakuseiFolderと、idouFilesを呼び出します。sakuseiFolderの引数はfolderNamesで、idouFilesの引数は配列taisyoFilesと変数documentFolder(「ドキュメントフォルダ」のFileオブジェクト)です。

関数の呼び出し(GAS)
seiriFiles関数の全体
seiFiles関数の全体
function seiriFiles() {
  const documentFolder = DriveApp.getFolderById("フォルダID");
  const files = documentFolder.getFiles();

  let hitothukimae = new Date();
  hitothukimae.setMonth(hitothukimae.getMonth() - 1);
  let taisyoFiles = []; //移動対象のファイルオブジェクトを格納する配列
  let folderNames = []; //作成するフォルダー名を格納する配列

  while (files.hasNext()) {
    let file = files.next();
    let lastUpdated = file.getLastUpdated();
    if (lastUpdated.getTime() < hitothukimae.getTime()) {
      taisyoFiles.push(file);
      folderNames.push(dayjs.dayjs(new Date(lastUpdated)).format("YYYYMM"));
    }
  }

  sakuseiFolder(folderNames);
  idouFiles(taisyoFiles,documentFolder);
}

sakuseiFolder関数の実装

seiriFiles関数より呼ばれるsakuseiFolder関数を実装します。この関数でfolderNamesの配列に格納されているフォルダー名を取り出し「old」フォルダの中にそのフォルダ名で、フォルダを作成します。

フォルダ名の重複チェック

まずfilter関数で、folderNamesに格納されているフォルダ名を抽出して重複がない様にチェックしながら、newlistという新しい配列を生成します。

sakuseiFolder関数の実装1

※スクリプトの解説は、「配列の重複を削除する方法」を参照願います。

[ newlistの確認 ]
sakuseiFolder関数の実装2
フォルダの作成

配列newListに格納されている名前でYYYYMMフォルダを作成します。但し、このスクリプトを2回以上実行した場合は、初回の実行で作成されたYYYYMMフォルダと同じフォルダが存在する可能性がある為、IF文で確認(存在チェック)して作成します。

sakuseiFolder関数の実装3
  • 「old」フォルダを取得します。
    const oldFolder = DriveApp.getFolderById(“フォルダID”);
  • フォルダが重複しない様にチェック
    if (!oldFolder.getFoldersByName(newlist[i]).hasNext()) {
        oldFolder.createFolder(newlist[i]);
    }

[ スクリプトの解説 ]
getFoldersByNameメソッドもFolderIteratorを返しますので、イテレータの処理をしてフォルダ名を取得しています。newlistの配列に格納されているフォルダ名を次、次と取り出し同じフォルダ名があれば、trueを返しますが、!の論理演算子がついている為、falseを返します。つまり同じフォルダ名が無い時にoldFolder.createFolder(newlist[i]);が、実行されます。
※詳細は、「存在チェックの方法」を参照願います。

sakuseiFolder関数の全体
sakuseiFolder関数の全体
function sakuseiFolder(folderNames) {
  const newlist = folderNames.filter(function (value, index, self) {
    return self.indexOf(value) === index;
  });
  const oldFolder = DriveApp.getFolderById("フォルダID");

  for (let i = 0; i < newlist.length; i++) {
    if (!oldFolder.getFoldersByName(newlist[i]).hasNext()) {
      oldFolder.createFolder(newlist[i]);
    }
  }
}

idouFiles関数の実装

seiriFiles関数より呼ばれるidouFiles関数を実装します。この関数でtaisyouFilesの配列に格納されているファイル名を取り出し、「old」フォルダの中の最終更新日と一致するフォルダ名のフォルダにファイルを移動させます。

idouFiles関数の実装
配列taisyoFilesからファイルの取り出し

for (let i=0; i<taisyoFiles.length; i++) {
引数で受け取った配列taisyoFiles(移動対象のファイルが格納されている)の要素数(taisyoFiles.length)だけ繰り返し処理を実行します。
let lastUpdated = taisyoFiles[i].getLastUpdated();
処理の中では、移動対象ファイルの最終更新日をgetLastUpdated()メソッドで、改めて取得して、

移動先のフォルダ名を取得

let taisyoFolder = oldFolder.getFoldersByName(name).next();
getFoldersByNameでフォルダ名を取得して、変数taisyoFolderに格納します。
※nameには、lastUpdatedから取得した最終更新日をYYYYMMのformatに変換する次のオーダーを記載します。
dayjs.dayjs(new Date(lastupdated)).format(“YYYYMM”))

ファイルの移動

taisyoFiles[i].moveTo(taisyoFolder);
配列taisyoFilesから取り出したファイルをmoveToメソッドでtaisyoFolderに移動させます。

スクリプト全体の掲載

スクリプト全体を掲載しておきます。

コード.gsの全体
function seiriFiles() {
  const documentFolder = DriveApp.getFolderById("フォルダID");
  const files = documentFolder.getFiles();

  let hitothukimae = new Date();
  hitothukimae.setMonth(hitothukimae.getMonth() - 1);
  let taisyoFiles = []; //移動対象のファイルオブジェクトを格納する配列
  let folderNames = []; //作成するフォルダー名を格納する配列

  while (files.hasNext()) {
    let file = files.next();
    let lastUpdated = file.getLastUpdated();
    if (lastUpdated.getTime() < hitothukimae.getTime()) {
      taisyoFiles.push(file);
      folderNames.push(dayjs.dayjs(new Date(lastUpdated)).format("YYYYMM"));
    }
  }

  sakuseiFolder(folderNames);
  idouFiles(taisyoFiles,documentFolder);
}
function sakuseiFolder(folderNames) {
  const newlist = folderNames.filter(function (value, index, self) {
    return self.indexOf(value) === index;
  });
  const oldFolder = DriveApp.getFolderById("フォルダID");

  for (let i = 0; i < newlist.length; i++) {
    if (!oldFolder.getFoldersByName(newlist[i]).hasNext()) {
      oldFolder.createFolder(newlist[i]);
    }
  }
}
function idouFiles(taisyoFiles,documentFolder) {
  const oldFolder = DriveApp.getFolderById("フォルダID")

  for (let i = 0; i < taisyoFiles.length; i++) {

    let lastUpdated = taisyoFiles[i].getLastUpdated();
    let taisyoFolder = oldFolder.getFoldersByName(dayjs.dayjs(new Date(lastUpdated)).format("YYYYMM")).next();
    taisyoFiles[i].moveTo(taisyoFolder);
  };
};

自動化スクリプトの実行

自動化スクリプトの実行1

スクリプトを初回実行するとき次のような承認画面が表示された場合は承認作業を行ってください。
手順はこちら

自動化スクリプトの実行2

スクリプトの実行結果は、以下の通りです。

ファイル整理自動化のイメージ(GAS)

承認手続き

スプレッドシートやフォームなどのサービスと連携させる処理を行うと初回起動時のみ承認作業が必要です。アクセス許可が必要なのは、アカウント毎ではなく、1つのアプリ(スプレッドシート毎)に対して必ず1度承認しないといけません。

権限を確認

最初に実行する場合は、認証が必要となります。

スクリプトの承認手順1

「詳細」をクリック

「詳細」から「無題のプロジェクト(安全ではないページ)に移動」をクリックします。

スクリプトの承認手順2

「許可」をクリック

スクリプトの承認手順3

配列の重複を削除する方法

GASで配列の重複要素を削除する方法(ユニーク要素の配列にする方法)をご紹介します。filterメソッドを適応して配列の重複を削除します。

filterメソッド

filterの中で特定の条件を与えて配列データを取得したい内容を「コールバック関数」に書くことで、任意のデータを抽出して新しい配列を生成します。
[ 基本構文 ]
var items = 配列データ;
items.filter(コールバック関数)

コールバック関数

コールバック関数は、3つの引数を受け取ることができます。
items.filter( function( value, index, array ) {
}

  • value:配列の値
  • index:配列のインデックス番号
  • array(self):現在処理している配列

※function(value,index,array)としていますが、変数名は任意です。

サンプルスクリプト

以下は、配列の重複除外スクリプトです。

配列の重複除外スクリプト(GAS)

繰り返し処理をしている最中の配列の値が第1引数に、第2引数にはインデックス、第3引数にはitems配列が入っている状態となります。function内部でのreturnでTrueかFalseを判定して、filterではTrueとしたものだけを残す様になっています。配列.indexOfで最初に見つけた文字列のインデックス情報を返します。最初のりんごはインデックス0なので、そのまま→0 === 0 =>True、最後のりんごはインデックス3となりますが、self.indexOf(りんご)とした場合には0となりますので、0 === 3 =>Falseとなり、最後のりんごは除外されています。

存在チェックの方法

Googleドライブ上ではすべてのフォルダやファイルにユニークなIDが付与されていますので、同じファイル名や同じフォルダ名を作成することができます。GoogleApps Scriptでフォルダやファイルを作成するにはcreateFolderやcreateFileを使用しますが、そのままだと同名のフォルダやファイルが作成されてしまいます。同じフォルダ内に同名のフォルダやファイルが作成されない様にするには同名のフォルダやファイルが存在しているか判定して存在していないときのみcreateFolderやcreateFileで作成することが必要です。ここでは、同名のフォルダやファイルの存在チェックの方法について解説します。

処理内容

「写真フォルダ」の中に、「picture202306」と「picture202304」のフォルダがあります。ここに、「picture202304」「picture202305」「picture202306」「picture202307」という名前のフォルダを作成しますが、既に存在するフォルダは作成しないようにします。

「写真」フォルダの構成(Googleドライブ)

つまり、既に存在する「picture202306」と「picture202304」のフォルダは作成せず、存在しない「picture202305」と「picture202307」のフォルダのみ作成します。

スクリプトの作成

存在チェックスクリプト(GAS)
function myFunction() {
  const folder = DriveApp.getFolderById("フォルダID");
  const newlist = ["picture202304", "picture202305", "picture202306", "picture202307"]

  // 存在チェック
  for (let a = 0; a < newlist.length; a++) {
    if (!folder.getFoldersByName(newlist[a]).hasNext()) {
      // フォルダがなければ作成
      folder.createFolder(newlist[a]);
      }
      // Logger.log(newlist[a]);
  }
}

newlistの配列を読み込む

for (let a = 0; a<newlist.length; a++) {
    // 読み込んだ時の処理
}

読み込んだ時の処理

if (!folder.getFolderByName(newlist[a]).hasNext()) {
    // 存在しなかった時の処理
}

存在しなかった時の処理

folder.createFolder(newlist[a]);

[ スクリプトの解説 ]
getFoldersByNameメソッドもFolderIteratorを返しますので、イテレータの処理をしてフォルダ名を取得しています。newlistの配列に格納されているフォルダ名を次、次と取り出し同じフォルダ名があればtrueを返しますが、!(論理演算子)がついている為、falseを返します。つまり、同じフォルダ名が無かった時に、createFolderを実行します。

スクリプトの検証

folder.createFolder(newlist[a]);存在しなかった時の処理をコメントアウトして、logを表示して確認してみます。

存在チェックスクリプトの検証(GAS)

存在しない「picture202305」と「picture202307」のフォルダのみが表示されました。

スクリプトの実行

スクリプトの検証ができましたので、コメントアウトを元に戻してスクリプトを実行します。

存在チェックスクリプトの実行1

存在しない「picture202305」と「picture202307」のフォルダが作成されました。

存在チェックスクリプトの実行2