Google App ScriptでGoogle Driveフォルダを(共有ドライブに)コピーする
なぜこのようなスクリプトが必要なのか?
Google Workspace では、個人向け(無料版)のGoogleDriveには無い、「共有ドライブ」という機能がある。
この共有ドライブは、組織内のメンバーが共同でファイルを管理するための便利な機能である。
個人の「マイドライブ」でも、ファイルやフォルダへの読み取り・書き込みアクセス権限を、他のユーザーに付与することができるが、共有ドライブは、より高機能で、組織内でファイル共有する時に必要な機能が備わっている。
組織では、メンバーの離職によりアカウントが削除されることがあるが、マイドライブに保存されているファイルは、アカウントが削除されると同時に消えてしまう。
そのため、組織内でのファイル共有には、共有ドライブを利用することが推奨されている。
そこで、一括でファイルを移動させたいことが、ときどきあるのだが... GoogleDriveではフォルダのコピー・移動には制限が多い。
コピー元 → コピー先 | ファイル単位の操作 | フォルダ単位の操作 |
---|---|---|
マイドライブ → 共有ドライブ | コピー、移動とも可能 | コピー、移動できない |
マイドライブ → マイドライブ | コピー、移動とも可能 | 移動のみ可能 ※ショートカット作成を促される |
そこで、、Google App Scriptを使って、マイドライブ内のフォルダを、共有ドライブにコピーするスクリプトを作成した。
スクリプトの概要
このスクリプトは、Google Drive APIを使用して、指定したフォルダを再帰的に、別のフォルダにコピーする。
今回は、マイドライブ内のフォルダを、共有ドライブにコピーするためにスクリプトを書いたが、マイドライブ内へのコピーにも使える。
function copyAll(){
doCopy('YYYYYYYYYYYYYYYYY', 'XXXXXXXXXXXXXXXXXX');
}
function doCopy(sourceFolderId, targetParentDriveOrFolderId) {
copyFolderRecursively(
DriveApp.getFolderById(sourceFolderId),
targetParentDriveOrFolderId
);
}
/**
* (オプション) フォルダ構造を維持して再帰的にコピーする関数
* copyMyDiveFolderToSharedDrive 関数の最後で copyFolderRecursively(sourceFolder, DEST_FOLDER_ID); を呼び出す
*
* @param {Folder} sourceFolder コピー元のフォルダオブジェクト
* @param {string} targetParentDriveOrFolderId コピー先の親フォルダオブジェクト
*/
function copyFolderRecursively(sourceFolder, targetParentDriveOrFolderId) {
const sourceFolderName = sourceFolder.getName();
Logger.log(`フォルダ処理開始: "${sourceFolderName}" (ID: ${sourceFolder.getId()}) -> 親ID: ${targetParentDriveOrFolderId}`);
// 1. コピー先に同名のフォルダを作成
const targetFolder = createFolderInFolder(targetParentDriveOrFolderId, newFolderName=sourceFolder.getName());
// 2. 作成したフォルダ内にファイルをコピー
const files = sourceFolder.getFiles();
while (files.hasNext()) {
const file = files.next();
const fileName = file.getName();
const fileId = file.getId();
try {
const copiedFile = copyFileToFolder(fileId, targetFolder.getId(), fileName);
Logger.log(` -> コピー成功: "${copiedFile.getName()}" (新しいID: ${copiedFile.getId()})`);
} catch (err) {
Logger.log(` -> エラー: ファイル "${fileName}" (ID: ${fileId}) のコピーに失敗。理由: ${err}`);
}
// Utilities.sleep(500); // 実行時間制限対策
}
// 3. サブフォルダに対して再帰的に処理
const subFolders = sourceFolder.getFolders();
while (subFolders.hasNext()) {
const subFolder = subFolders.next();
// 再帰呼び出し
copyFolderRecursively(subFolder, targetFolder.getId()); // 新しく作ったフォルダを次の親IDとして渡す
}
Logger.log(`フォルダ処理完了: "${sourceFolderName}"`);
}
/**
* 指定したフォルダIDのフォルダの下に新しいフォルダを作成する
*
* @param {string} parentFolderId 親フォルダのID
* @param {string} newFolderName 作成するフォルダ名
* @return {Folder} 作成したフォルダのオブジェクト
*/
function createFolderInFolder(parentFolderId, newFolderName) {
try {
var parentFolder = DriveApp.getFolderById(id=parentFolderId);
var newFolder = parentFolder.createFolder(newFolderName);
Logger.log('フォルダを作成しました: ' + newFolder.getName() + ' (ID: ' + newFolder.getId() + ')');
return newFolder;
} catch (e) {
Logger.log('フォルダの作成に失敗しました: ' + e.toString());
throw e;
}
}
/**
* 指定したファイルを指定のフォルダにコピーし、名前を変更して保存する
*
* @param {string} fileId コピー元ファイルのID
* @param {string} destFolderId コピー先フォルダのID
* @param {string} newFileName コピー後のファイル名
* @return {File} コピーしたファイルのオブジェクト
*/
function copyFileToFolder(fileId, destFolderId, newFileName) {
try {
var file = DriveApp.getFileById(fileId);
var destFolder = DriveApp.getFolderById(destFolderId);
var copiedFile = file.makeCopy(newFileName, destFolder);
Logger.log('ファイルをコピーしました: ' + copiedFile.getName() + ' (ID: ' + copiedFile.getId() + ')');
return copiedFile;
} catch (e) {
Logger.log('ファイルのコピーに失敗しました: ' + e.toString());
throw e;
}
}
こういうのって生成AIが得意だよね?
...と考えていたのだが、GASのお勉強が足りなかったのか、ちっとも上手くいかなかった。(Gemini 2.5 Pro
, o4-mini-high
)
結局、o4-mini-high
に、関数1個ずつ書かせてテストして...を繰り返して、作った。
関数単位だと動くものを書かせることができるし、よくあるタイプのスクリプトだと思うのだが、一括で生成できないのは不思議。
Discussion