GoogleAppsScriptで、Googleドライブのファイルやフォルダを扱う場合のGASでの扱い方について解説をします。「GoogleAppsScriptの使い方(基本的な使い方)」では、スプレッドシートに紐づいたコンテナバインドスクリプトを中心に解説をしました。が、今回は、Googleドライブに対して操作を行う、ドライブ上で独立して動作するスタンドアロンスクリプトについて解説をします。実務で良くあるフォルダやファイルの整理を自動化のスクリプトを作成しながら、Googleドライブに対する操作をご紹介します。
Googleドライブの扱い方
GASで、Googleドライブのファイルやフォルダを扱う場合は、DriveAppクラスを使います。DriveAppクラスの各種メソッドを使って各種操作を行います。
以下のようなGoogleドライブの構成を例として、フォルダの取得、ファイルの取得のメソッドをご紹介します。
フォルダの取得
まず、マイドライブの中の「業務フォルダ」を取得してみます。
フォルダの取得方法
GASでGoogleドライブのフォルダを取得する場合は、DriveAppのgetFolderByIdメソッドを使います。フォルダIDであればGoogleドライブでそのフォルダを開いている際のURLの以下の部分です。
DriveAppクラスからフォルダを取得する。
function myFunction() {
const folder = DriveApp.getFolderById("フォルダID");
console.log(folder.getName());
}
スクリプトで使用されているgetNameメソッドはフォルダ名を取得するメソッドです。
ファイルの取得
次に、「業務フォルダ」内に保存されていたファイル「シートの取得用」を取得してみます。
ファイルの取得方法
Googleドライブのファイルを取得する場合は、DriveAppのgetFileByIdメソッドを使います。
function myFunction() {
const file = DriveApp.getFileById("ファイルID");
console.log(file.getName());
}
※ファイルIDは、以下の通り
全てのファイルを取得する。
「const files = DriveApp.getFiles();」が、Googleドライブのすべてのファイルを取得するスクリプトになります。取得したデータは「FileIterator(ファイルイテレータ)」という形式です。イテレータを使えばデータの集まりに対して、「次」、「次」と抽象的に命令するだけで、順番に各データを取得することができます。
function myFunction() {
const files = DriveApp.getFiles();
while (files.hasNext()) {
const file = files.next();
console.log(file.getName());
}
}
特定のフォルダーの中のファイルを全て取得する。
const gyomuFolder = DriveApp.getFolderById("フォルダID");
function myFunction() {
const files = gyomuFolder.getFiles();
while (files.hasNext()) {
const file = files.next();
console.log(file.getName());
}
}
ファイル整理の自動化
それでは、実際に実務で良くある「フォルダの整理」「ファイルの整理」をスクリプトを作成して自動化してみます。
ファイル整理自動化の内容
「ドキュメント」フォルダ内のファイルの内、ファイルの最終更新日が1ヶ月以上前のファイルについては、「old」フォルダ内に新しく「年月」のフォルダを作成して移動させます。
※わかりやすいようにファイル名は、最終更新日としています。
ファイル整理自動化の準備
作成する関数(スクリプト)
seiriFiles関数
「ドキュメント」フォルダ内のファイルを調べて、最終更新日が1ヶ月以上前のファイルを取得します。
sakuseiFolder関数
seiriFiles関数から呼び出され、移動対象となるファイルを取得して「old」フォルダ内に「年月」フォルダを作成します。
idouFiles関数
seiriFiles関数から呼び出され、sakuseiFolder関数で作成した「年月」フォルダに移動対象のファイルを移動させます。
ライブラリーの導入
このスクリプトでは、日付データを扱いますので「dayjs」というライブラリーを導入します。
dayjsのスクリプトIDは、
1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB
自動化スクリプトの作成
それでは、スクリプトを作っていきます。
スクリプトエディターを開く
今回は、Googleドライブ上で独立して動作するスタンドアロンスクリプトを作成します。
seiriFiles関数の実装
「ドキュメント」フォルダ内のファイルを調べて、最終更新日が1ヶ月以上前のファイルを取得します。プロジェクト名は、「ファイルの整理」、ファイル名はコード.gs、関数名はseiriFilesとします。
フォルダ内のファイルの取得
getFiolderByIdメソッドで「ドキュメント」フォルダを取得してgetFilesメソッドで、「ドキュメント」フォルダ内のファイルを全て取得してfilesに格納します。
function seiriFiles() {
const documentFolder = DriveApp.getFolderById("フォルダID");
const files = documentFolder.getFiles();
3行目の「getFiles();」が、「ドキュメント」フォルダ内の全てのファイルを取得するスクリプトになります。取得したデータは「FileIterator(ファイルイテレータ)」という形式です。イテレータを使えばデータの集まりに対して「次」「次」と抽象的に命令するだけで、順番に各データを取得することができます。
1ヶ月前の日付の取得
現在の日付から1ヶ月前の日付を取得します。
dateオブジェクト.setMonth(引数)で、dateオブジェクトの月から1ヶ月前の日付をセットします。
留意:
taisyoFiles = [ ];は移動対象のファイルオブジェクトを格納する配列で、
folderNames=[ ];は、作成するフォルダ名を格納する配列となります。
[ hitothukimaeの内容を確認します。]
現在日付が2023/07/07ですので、1ヶ月前の2023/06/07となっています。
注意:
formatは、”yyyy/MM/DD”ではなく、”yyyy/MM/dd”となりますので、ご注意下さい。”yyyy/MM/DD”だと、以下のように158と変な数値となってしまいます。
最終更新日のチェックと対象ファイルの格納
次に、whileとhasNextメソッド、nextメソッドで「ドキュメント」フォルダから取得したファイルの最終更新日が1ヶ月以上前か否かをチェックします。
※ファイルの最終更新日を取得するには、getLastUpdated()メソッドを使用します。
最終更新日(lastUpdated.getTime())の方が、現在日付の1ヶ月前の日付(hitothukimae.getTime())より前の日付だった場合は配列(taisyoFiles)にファイルを格納します。そして最終更新日からYYYYMM形式の文字列を生成してfolderNamesに格納します。
※getTime() ではDate型の日付をミリ秒に変換した数値を返却してくれます。
関数の呼び出し
sakuseiFolderと、idouFilesを呼び出します。sakuseiFolderの引数はfolderNamesで、idouFilesの引数は配列taisyoFilesと変数documentFolder(「ドキュメントフォルダ」のFileオブジェクト)です。
seiriFiles関数の全体
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という新しい配列を生成します。
※スクリプトの解説は、「配列の重複を削除する方法」を参照願います。
フォルダの作成
配列newListに格納されている名前でYYYYMMフォルダを作成します。但し、このスクリプトを2回以上実行した場合は、初回の実行で作成されたYYYYMMフォルダと同じフォルダが存在する可能性がある為、IF文で確認(存在チェック)して作成します。
- 「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関数の全体
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」フォルダの中の最終更新日と一致するフォルダ名のフォルダにファイルを移動させます。
配列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に移動させます。
スクリプト全体の掲載
スクリプト全体を掲載しておきます。
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つのアプリ(スプレッドシート毎)に対して必ず1度承認しないといけません。
権限を確認
最初に実行する場合は、認証が必要となります。
「詳細」をクリック
「詳細」から「無題のプロジェクト(安全ではないページ)に移動」をクリックします。
「許可」をクリック
配列の重複を削除する方法
GASで配列の重複要素を削除する方法(ユニーク要素の配列にする方法)をご紹介します。filterメソッドを適応して配列の重複を削除します。
filterメソッド
filterの中で特定の条件を与えて配列データを取得したい内容を「コールバック関数」に書くことで、任意のデータを抽出して新しい配列を生成します。
[ 基本構文 ]
var items = 配列データ;
items.filter(コールバック関数)
コールバック関数
コールバック関数は、3つの引数を受け取ることができます。
items.filter( function( value, index, array ) {
}
- value:配列の値
- index:配列のインデックス番号
- array(self):現在処理している配列
※function(value,index,array)としていますが、変数名は任意です。
サンプルスクリプト
以下は、配列の重複除外スクリプトです。
繰り返し処理をしている最中の配列の値が第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」という名前のフォルダを作成しますが、既に存在するフォルダは作成しないようにします。
つまり、既に存在する「picture202306」と「picture202304」のフォルダは作成せず、存在しない「picture202305」と「picture202307」のフォルダのみ作成します。
スクリプトの作成
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を表示して確認してみます。
存在しない「picture202305」と「picture202307」のフォルダのみが表示されました。
スクリプトの実行
スクリプトの検証ができましたので、コメントアウトを元に戻してスクリプトを実行します。
存在しない「picture202305」と「picture202307」のフォルダが作成されました。