[GAS] Goole Apps Script を使って GoogleDrive のファイルを別アカウントの GoogleDrive に一括コピーする

個人の GoogleDrive に入れていたファイルを GoogleWorkspace の共有ドライブに移行したのですが、**複数の所有者がいたため単純なドラッグ・ドロップで移動ができませんでした。**

所有者問題はファイルのコピーを作成することで回避できるのですが、ひとつずつやるのは骨が折れるので GAS を使って一括コピーしました。

# やり方

GooleDrive にアクセスして、`新規 ＞ その他 ＞ Google Apps Script` を選び、プロジェクトを作成します。

下記のスクリプトを記述し、`コピー元フォルダ ID`、`コピー元フォルダ ID` を環境に合わせて書き換えます。

保存をしたら `実行` すれば処理を開始します。

## 注意点

- 必ずテスト用フォルダを作って一度テストしてください。
- あまりにも大量にファイルがあると落ちるかもしれません。
- 筆者は4階層200ファイルくらいでしかテストしてません。
- フォルダ毎に実行したほうが安全だと思います。
- うっかりファイルが消えても補償できません。自己責任でお願いします。
- 不具合があったら教えてください。


## スクリプト

`コピー元フォルダ ID`、`コピー元フォルダ ID` を環境に合わせて書き換えます。

フォルダIDはURLの最後の部分です。
`https://drive.google.com/drive/folders/1234567890ABCDEFGabcdefg` だったら
`1234567890ABCDEFGabcdefg`　です。

`copyMode` に同名ファイルが存在した時の動作を指定してください。

`MODE_OVERRIDE` を選択しても別のアカウントがオーナーのファイルは削除できないので、そのファイルについては `MODE_ADD_NUMBER` が実行されます。

```js
/**
 * GoogleDriveのファイルを下層もまとめてコピーする
 * 同じファイルがあった場合は「上書き / 無視 / 日付が新しいほうを残す」を選べる
 * GoogleDriveは仕様上同名のファイルが存在できるが、このスクリプトでは1つしか許可しない前提
 */

// コピー元フォルダ ID
const src = 'xxxxxxxxxxxxxxxxxxxxxxx';

// コピー先フォルダ ID
const dist = 'xxxxxxxxxxxxxxxxxxxxxxx';

// 同じファイルがあった時の動作
const MODE_OVERRIDE   = 1;  // 上書き。権限がなければ MODE_ADD_NUMBER になる
const MODE_CANCEL     = 2;  // 無視
const MODE_NEWDAY     = 3;  // 新しければコピーする
const MODE_ADD_NUMBER = 4;  // 数字を付ける
const copyMode = MODE_OVERRIDE;

/**
 * 開始
 */
function myFunction() {
  copyFolder(src, dist, '');
}

/**
 * フォルダをコピー
 */ 
function copyFolder(srcFolderId, distFolderId, pathStr){
  const srcFolderObj = DriveApp.getFolderById(srcFolderId);
  const distFolderObj = DriveApp.getFolderById(distFolderId);
  const newPathStr = pathStr + '/' + srcFolderObj.getName();

  console.log(newPathStr);

  // フォルダ一覧取得して作成
  const folderss = srcFolderObj.getFolders();

  for (let i = 0; folderss.hasNext(); i++) {
    const folder = folderss.next();
    const folderName = folder.getName();
    let newFolder;

    // フォルダが存在しなければ作成する
    const folderIterator = distFolderObj.getFoldersByName(folderName);

    if (folderIterator.hasNext()) {
      newFolder = folderIterator.next();
    } else {
      newFolder = distFolderObj.createFolder(folderName);
    }
    // さらに下層に入る
    copyFolder(folder.getId(), newFolder.getId(), newPathStr);
  }
  // ファイルコピー
  copyFiles(srcFolderObj, distFolderObj);
}

/**
 * ファイルをコピー
 */
function copyFiles(srcFolderObj, distFolderObj){
  const files = srcFolderObj.getFiles();

  for (let i = 0; files.hasNext(); i++) {
    const file = files.next();
    const fileName = file.getName();

    const existsFiles = distFolderObj.getFilesByName(fileName);

    // 既存ファイルが存在したらモードによって動作を変える
    if (existsFiles.hasNext()){

      switch(copyMode) {
        case MODE_OVERRIDE:
          // 同名があったら上書きする
          copyOverride(distFolderObj, file, fileName, existsFiles.next());
          break;
        case MODE_ADD_NUMBER:
          // 同名があったら番号を付ける
          copyAddNumber(distFolderObj, file, fileName);
          break;
        case MODE_NEWDAY:
          // 新しければコピーする
          copyNewDay(distFolderObj, file, fileName, existsFiles.next());
          break;
        case MODE_CANCEL:
          // 同名があったらコピーしない
          break;
        default:
      }

    } else {
      // 同名ファイルが存在しないのでコピーする
      file.makeCopy(fileName, distFolderObj);
    }
  }
}

/**
 * 既存ファイルを削除してコピー
 */
function copyOverride(distFolderObj, srcFile, srcFileName, existsFile) {
  // 同名ファイルを削除してからコピー
  // 削除失敗したら番号追加でコピー
  try {
    existsFile.setTrashed(true);
    srcFile.makeCopy(srcFileName, distFolderObj);
  } catch(e) {
    copyAddNumber(distFolderObj, srcFile, srcFileName);
  }
}

/**
 * 番号を付けてコピー
 */
function copyAddNumber(distFolderObj, srcFile, srcFileName) {
  let count = 1;
  let newFileName, existsFiles;

  // 既存ファイルが存在しなくなるまで番号を追加
  do {
    newFileName = srcFileName + '_' + count;
    existsFiles = distFolderObj.getFilesByName(newFileName);
    count += 1;
  } while (existsFiles.hasNext());

  srcFile.makeCopy(newFileName, distFolderObj);
}

/**
 * 新しければコピーする
 */
function copyNewDay(distFolderObj, srcFile, srcFileName, existsFile) {
  const srcUpdate = srcFile.getLastUpdated();
  const existsUpdate = existsFile.getLastUpdated();

  if (srcUpdate > existsUpdate) {
    copyOverride(distFolderObj, srcFile, srcFileName, existsFile);
  }
}
```