0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【GAS】Googleフォームの添付ファイルを自動整理 専用フォルダ作成&ファイル名変更で管理をラクにする②

0
Last updated at Posted at 2026-05-22

今までの記事一覧

  1. GASでGoogleフォーム回答を取得するなら lastRow?(e)?試してみた
  2. onFormSubmit(e)を手動実行でデバッグする方法
  3. どっちを使う?onFormSubmit(e)の values と namedValues の違いと使い分け
  4. onFormSubmit(e) の e.values 配列順のしくみ
  5. Googleフォームで質問を変えても壊れない!cleanFormData(e)でnamedValues防御力をアップ
  6. Googleフォームの質問変更に負けない!「部分一致」と「秘密の暗号」でcleanFormData(e)の防御力を鉄壁に
  7. 手動コピペはもう卒業!Googleフォームの回答別に処理を自動仕分け
  8. Googleフォームで同時に大量送信されても踏ん張る!LockServiceで順番制御!try - catch - finally でバトンを繋げ!
  9. LockServiceでは順番は守れない?受付番号で順序を保証する方法
  10. 行削除も怖くない!PropertiesServiceでGoogleフォームに「ズレない・飛ばない」受付番号を実装する
  11. Googleフォームの「回答を編集」対策!新規と修正を識別してシートへの二重登録/番号ズレを防ぐ
  12. Googleフォームの添付ファイルを自動整理 専用フォルダ作成&ファイル名変更で管理をラクにする①
  13. Googleフォームの添付ファイルを自動整理 専用フォルダ作成&ファイル名変更で管理をラクにする②  <この記事

前提

この記事は、フォーム回答を保存している スプレッドシート側のGAS を前提にしています。
トリガーは以下を設定しています。

  • スプレッドシートから
  • フォーム送信時

前回のおさらい

前回はフォームで添付ファイルを送信した場合に、新しいフォルダを作成し、添付ファイルをそのフォルダに移動し、ファイル名を変更し、権限変更する処理を作りました。
しかしファイルを送信すべき質問が複数あったり、1つの質問に複数ファイルを送信された場合にうまく動きませんでした。

そこで今回は「複数質問・複数ファイルの自動仕分け」ができるように改造してみたいと思います。

やりたいこと

  1. 親フォルダ作成:送信ごとに「受付番号_送信者」のフォルダを作成(前回と同じ)
  2. 子フォルダ作成:親フォルダの中に「黒猫」「キジトラ」など質問名のフォルダを作成
  3. ファイル移動:対応する子フォルダにファイルを移動する
  4. ファイル名変更:「受付番号_子フォルダ名_ファイル番号」にファイル名変更。ファイル番号は子フォルダ内での順番にする
  5. 受付シートリンク:受付シートには子フォルダのリンクを入れる

テスト用にこんなフォームを作りました。
image.png

ファイルの仕分けは少し大がかりになるので、プログラムの役割をスッキリ分けるために、関数を2つに分離して作っていきます。

  • メイン関数(form_edited):フォーム送信を受け付け、受付番号を発行し、今回の保存先となる「親フォルダの作成・準備」までを担当します。
  • ファイル仕分け関数(processFiles):メイン関数から親フォルダを受け取り、ファイルの仕分けをします。

まずは基本形となるコードからみていきましょう。

メイン関数

function form_edited(e) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  
  const sheet = e.range.getSheet();  //フォーム回答が入力されたシート
  const row = e.range.getRow();  //フォーム回答が入力された行
  const column = e.values.length;  // 質問の列数を取得(イベントオブジェクトから取得)
  const sheet_uketuke = ss.getSheetByName("受付シート");

  // 「今書き込まれた行」の純粋な回答データと質問見出しを取得
  const formData = sheet.getRange(row, 1, 1, column).getValues();
  const headers = sheet.getRange(1, 1, 1, column).getValues()[0];

  ////  受付番号の列を取得し、新規の番号を発行して書き込む  ////
  //セルに名前(受付番号)を付けておくことでgetRange("受付番号")で呼び出すことができる
  const column_uketuke = sheet.getRange("受付番号").getColumn();
  
  //プロパティサービスに受付番号を保存しておき、必要に応じて呼び出したり書き込んだりする
  const lastId = Number(PropertiesService.getScriptProperties().getProperty("RECEIPT_ID"));
  const currentId = lastId + 1;
  PropertiesService.getScriptProperties().setProperty("RECEIPT_ID", String(currentId));

  sheet.getRange(row, column_uketuke).setValue(currentId);

  // 受付シートの末尾にタイムスタンプと名前を転記し、書き込み先の行番号を確定する
  const targetRow = sheet_uketuke.getLastRow() + 1;
  sheet_uketuke.getRange(targetRow, 2, 1, 2).setValues([formData[0].slice(0, 2)]);
  sheet_uketuke.getRange(targetRow, 1).setValue(currentId);     

  // ★【準備】保存先の親フォルダを指定(ご自身の環境のIDに書き換えてください)
  const parentFolder = DriveApp.getFolderById("******");//<実際の保存先フォルダIDを入れてください
  const nameIndex = headers.indexOf("あなたのお名前");  //<実際の質問名を入れてください
  
  //名前が入力されていればその名前、入力されていなければ「名無し」
  const userName = (nameIndex !== -1 && formData[0][nameIndex]) ? formData[0][nameIndex] : "名無し";

  // 今回の専用メインフォルダ(親フォルダ)を作成
  const mainFolderName = "受付番号_" + currentId + "_" + userName;
  const mainFolder = parentFolder.createFolder(mainFolderName);

  // ファイル仕分け関数を呼び出します
  processFiles(formData[0], currentId, headers, sheet_uketuke, targetRow, mainFolder);
}

受付番号PropertiesService.getScriptProperties().getProperty("RECEIPT_ID")についてはこのシリーズの10番目の記事をご参照ください。

ファイル仕分け関数

function processFiles(rowData, currentId, headers, sheet_uketuke, targetRow, mainFolder) {
  //スプレッドシートの各列をループでチェック
  for (let colIndex = 0; colIndex < rowData.length; colIndex++) {
    let cellValue = rowData[colIndex];

    // その列の中身が「GoogleドライブのURL」だったら処理を開始
    if (typeof cellValue === "string" && cellValue.includes("drive.google.com")) {
      let questionName = headers[colIndex];
      
      // 質問項目名の「子フォルダ(例:黒猫)」をメインフォルダの中に作成
      let subFolder = mainFolder.createFolder(questionName);
      
      // 受付シートの該当セルに、今作った子フォルダへのハイパーリンクを直接書き込む
      let targetColumn = colIndex + 2; // B列(2列目)から順に並ぶため +2
      sheet_uketuke.getRange(targetRow, targetColumn).setValue('=HYPERLINK("' + subFolder.getUrl() + '", "📁 ' + questionName + '")');
      
      // フォームの複数ファイルは「, 」で区切られているので配列に分解
      let urls = cellValue.split(", ");

      // ファイルの数(URLの数)だけ繰り返す
      for (let urlIndex = 0; urlIndex < urls.length; urlIndex++) {
        let url = urls[urlIndex];
        let fileId = url.match(/[-\w]{25,}/);
        
        if (fileId) {
          let file = DriveApp.getFileById(fileId);
          
          // 作成した子フォルダへファイルを移動
          file.moveTo(subFolder); 

          // 元のファイル名から拡張子(.jpgなど)を取得
          let originalName = file.getName();
          let extension = "";
          if (originalName.includes(".")) {
            extension = originalName.substring(originalName.lastIndexOf("."));
          }
          
          // 新しいファイル名を決定(受付番号_質問名_枝番.拡張子)
          let newName = currentId + "_" + questionName + "_" + (urlIndex + 1) + extension;
          file.setName(newName);
        }
      }
    }
  }
}

動作確認

動かしてみましょう。

フォームの回答シートはこのようにそれぞれファイルのURLが入ります。

受付シートにはちゃんとフォルダのリンクが入ってますね。

ドライブのほうも、きちんと仕分けされています。

うん、ねこちゃんカワユス★
これで基本形ができました。

file.moveTo(subFolder) を使うと、ファイルはフォームの標準保存フォルダから完全に移動して消えます。
もし「フォーム側(元フォルダ)にもバックアップとして残しておきたい!」という場合は、moveTo の代わりに file.makeCopy(subFolder) を使うと、コピーが仕分けされるようになります。

◆コピーをすることでドライブのストレージ圧迫が気になる方は、
この部分 ↓ を消して

file.moveTo(subFolder); 
file.setName(newName);

こう ↓ 変えると、元フォルダにファイルがそのまま残り、子フォルダ側にショートカットがリネームして作成されるようになります。

subFolder.createShortcut(fileId).setName(newName);

この場合、GASコードのファイル重複判定ロジックは書き換えが必要になります。

let fileId = url.match(/[-\w]{25,}/);
if (fileId) {
  let file = DriveApp.getFileById(fileId);

この部分で url.match() の戻り値は「配列」になります。ただし、GAS の DriveApp.getFileById(id) は、要素が1つだけの配列を渡された場合は内部で自動的に文字列へ変換(暗黙の型変換)して処理してくれるため、このままでも動作します。
気になる方は、このように

let fileIdMatch = url.match(/[-\w]{25,}/);
if (fileIdMatch) {
  let fileId = fileIdMatch[0];
  let file = DriveApp.getFileById(fileId);

match() の結果から0番目の要素を取り出しておくと、より安全で読みやすくなります。

さて、ねこエネルギーが充填できたので、ここからがんばってフォームの新規送信と修正送信の条件分岐を作っていきます。

新規送信と修正送信で条件分岐

フォームが修正送信されたとき(回答の編集機能が使われたとき)は、以下の処理をします。

  1. 受付番号で該当の親フォルダを特定する
  2. ファイルの追加があれば該当の子フォルダに追加する
  3. 受付シートを上書きする

「1」のフォルダ特定をどうするかが悩みどころですが、今回は新規送信時に「フォームの回答」シートの「受付番号」を入れる列のすぐ右隣へ、親フォルダのIDをメモしておく設計にします。これなら修正時にも一発でIDを取得できます。

新規か修正かの見分け方と処理

新規送信(受付番号のセルが空っぽ)
 → 新しく受付番号を発行し、親フォルダを新規作成後、右隣のセルにフォルダIDを書き込みます。
修正送信(受付番号がすでに入っている)
 → その受付番号をそのまま使い、右隣のセルからフォルダIDを取得して再利用します。

これをふまえて、条件分岐を取り入れたメイン関数がこちらです。

メイン関数

function form_edited(e) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = e.range.getSheet();  // フォーム回答が入力されたシート
  const row = e.range.getRow();      // フォーム回答が入力された行
  const column = e.values.length;    // 質問の列数を取得(イベントオブジェクトから取得)
  const sheet_uketuke = ss.getSheetByName("受付シート");

  // 「今書き込まれた行」の純粋な回答データと質問見出しを取得
  const formData = sheet.getRange(row, 1, 1, column).getValues();
  const headers = sheet.getRange(1, 1, 1, column).getValues()[0];

  // 受付番号の列位置を取得
  const column_uketuke = sheet.getRange("受付番号").getColumn();
  //★受付番号の右隣を「フォルダID保存列」とする
  const column_folderId = column_uketuke + 1; 

  // ★すでに受付番号があるか確認(空なら新規、あれば修正)
  const existingId = sheet.getRange(row, column_uketuke).getValue();
  
  let currentId = "";
  let mainFolder = null;
  let targetRow = null;

   // ★親フォルダを新規作成(サブ関数に入っていたけどメイン関数に移動)
  const parentFolder = DriveApp.getFolderById("******"); // <親フォルダのIDに書き換えてください

  //★新規か修正か条件分岐
  if (!existingId) {
    //// 【新規送信の処理】 ////
    // プロパティサービスから新しい受付番号を発行
    const lastId = Number(PropertiesService.getScriptProperties().getProperty("RECEIPT_ID"));
    currentId = lastId + 1;
    PropertiesService.getScriptProperties().setProperty("RECEIPT_ID", String(currentId));

    // 回答シートに受付番号を書き込み
    sheet.getRange(row, column_uketuke).setValue(currentId);

    const nameIndex = headers.indexOf("あなたのお名前"); //<実際の質問名を入れてください
    //名前が入力されていればその名前、入力されていなければ「名無し」
    const userName = (nameIndex !== -1 && formData[0][nameIndex]) ? formData[0][nameIndex] : "名無し";
    // 今回の専用メインフォルダを作成
    const mainFolderName = "受付番号_" + currentId + "_" + userName;
    mainFolder = parentFolder.createFolder(mainFolderName);

    // 作成した親フォルダのIDを、受付番号の右隣にメモしておく
    sheet.getRange(row, column_folderId).setValue(mainFolder.getId());

    // 受付シートの末尾に新規追加し、行番号を確定
    targetRow = sheet_uketuke.getLastRow() + 1;
    sheet_uketuke.getRange(targetRow, 1).setValue(currentId);

  } else {
    //// 【修正送信の処理】 ////
    currentId = existingId;

    // 右隣のセルから、初回にメモしたフォルダIDを呼び出してフォルダを特定
    const savedFolderId = sheet.getRange(row, column_folderId).getValue();
    try {
      mainFolder = DriveApp.getFolderById(savedFolderId);
    } catch(err) {
      // 万が一フォルダが削除されていた場合のセーフティ
      mainFolder = parentFolder.createFolder("【再作成】受付番号_" + currentId);
      sheet.getRange(row, column_folderId).setValue(mainFolder.getId());
    }

    // 見つかったかどうか(-1かそれ以外か)で明確に分ける
    const uketukeIds = sheet_uketuke.getRange(1, 1, sheet_uketuke.getLastRow(), 1).getValues().flat();
    const foundIndex = uketukeIds.indexOf(Number(currentId));
    
    if (foundIndex === -1) {
      // 受付シートに見つからなければ末尾に追加
      targetRow = sheet_uketuke.getLastRow() + 1;
      sheet_uketuke.getRange(targetRow, 1).setValue(currentId);
    } else {
      // 見つかった場合は該当行を特定(インデックスは0から始まるので +1)
      targetRow = foundIndex + 1;
    }
  }

  // 受付シートの基本情報(B, C列など)を上書き転記(新規・修正共通)
  sheet_uketuke.getRange(targetRow, 2, 1, 2).setValues([formData[0].slice(0, 2)]);

  // ★ファイル仕分け関数を実行(特定したメインフォルダと受付シートの行番号を渡す)
  processFiles(formData[0], currentId, headers, sheet_uketuke, targetRow, mainFolder);
}

メイン関数の中にif-elseによる分岐ができました。
ifの中では新規送信用のフォルダ作成とフォルダIDのメモ、
elseではメモしたフォルダIDから該当フォルダの特定と上書き対象行の割り出しが行われています。

フォルダ仕分け関数

主フォルダの準備ができたら、それをファイル仕分け関数にパスします。
仕分け関数でも、修正の場合の子フォルダの使いまわしや、子フォルダに既にあるファイルが重複しないようガード処理を入れています。

function processFiles(rowData, currentId, headers, sheet_uketuke, targetRow, mainFolder) {
  //各列をループでチェック
  for (let colIndex = 0; colIndex < rowData.length; colIndex++) {

    let cellValue = rowData[colIndex];
    //ドライブのURLが含まれている列だったら処理開始
    if (typeof cellValue === "string" && cellValue.includes("drive.google.com")) {
      let questionName = headers[colIndex];
      let subFolder = null;

      // 既存の子フォルダがあるか探す
      let subFolders = mainFolder.getFoldersByName(questionName);
      if (subFolders.hasNext()) {
        subFolder = subFolders.next();  //すでにあればその子フォルダを再利用
      } else {
        subFolder = mainFolder.createFolder(questionName);  //なければ新規作成
      }

      // 受付シートの該当セルに子フォルダへのリンクを書き込み(上書き)
      let targetColumn = colIndex + 2;
      sheet_uketuke.getRange(targetRow, targetColumn)
          .setValue(
          '=HYPERLINK("' +
          subFolder.getUrl() +
          '", "📁 ' +
          questionName +
          '")'
        );

      // URL分解(フォームの複数ファイルは「, 」で区切られているので配列に分解)
      let urls = cellValue.split(", ");

      // 子フォルダ内にすでに存在するファイルIDの一覧
      let existingFileIds = [];
      let files = subFolder.getFiles();
      while (files.hasNext()) {
        let existingFile = files.next();
        existingFileIds.push(existingFile.getId());
      }

      // 添付されたURLの数だけループ処理
      for (let urlIndex = 0; urlIndex < urls.length; urlIndex++) {
        let url = urls[urlIndex];
        let fileIdMatch = url.match(/[-\w]{25,}/);
        if (!fileIdMatch) continue;
        let fileId = fileIdMatch[0];

        // すでに子フォルダ内に存在するファイルIDならスキップ
        if (existingFileIds.includes(fileId)) {
          continue;
        }

        let file = DriveApp.getFileById(fileId);

        // 現在フォルダ内にあるファイル数をもとに枝番を決定
        let fileNumber = existingFileIds.length + 1;

        file.moveTo(subFolder);

        let originalName = file.getName();
        //拡張子取得
        let extension =
          originalName.includes(".")
            ? originalName.substring(originalName.lastIndexOf("."))
            : "";
        //新しい名前=受付番号_質問名_ファイル番号_拡張子
        let newName =
          currentId +
          "_" +
          questionName +
          "_" +
          fileNumber +
          extension;

        file.setName(newName);

        // 配列更新
        existingFileIds.push(fileId);
      }
    }
  }
}

これで修正送信で何度上書きされても、フォルダが無限増殖せず、既存のファイルがダブったり消えたりすることなく管理できるようになりました。

動作確認

では実際に動かしてみましょう。

▶ 新規送信
まずは最初の送信となる「新規送信」のテストです。
「三毛野 サビ美」という名前で、茶トラの質問に写真を1枚だけ添付して送信してみます。

バッチリ成功しました。
受付シートを見ると、自動で発行された「受付番号5」の行が作られ、茶トラの列にフォルダのリンクがしっかり書き込まれています。
そのリンクをクリックして実際のGoogleドライブを見に行くと、「受付番号_5_三毛野 サビ美」という親フォルダの中に「茶トラ」という子フォルダが自動生成され、その中に「5_茶トラ_1.jpg」と綺麗にリネームされた茶トラ猫の写真がしっかり格納されていました。

▶ 修正送信
続いて、本題である「修正送信」のテストです。
先ほどの受付番号5番(三毛野 サビ美さん)の回答編集画面を開き、新しくキジトラの写真を1枚追加し、さらに茶トラの写真ももう1枚追加(合計2枚)して再送信してみます。

うまくいきました。
上の受付シート画像を確認すると、「受付番号5」の行が修正され、新しく回答した「キジトラ」列に子フォルダへのリンクが追加されています。

Googleドライブの中にも、このようにキジトラ・茶トラともきちんと写真が追加されています。

新規送信時の「5_茶トラ_1.jpg」は移動も改名もせず、追加された2枚目の写真が枝番「2」を割り振られて「5_茶トラ_2.jpg」として格納されました。

まとめ

今回は、Googleフォームでファイル送信された場合のファイル整理第2弾として、「複数質問・複数ファイル」が送信された場合の仕分けロジックと、実運用では避けて通れない「修正送信(回答の編集)」への対策をGASで実装しました。

  • メイン関数と仕分け関数で役割分担
  • 使っていないセルにフォルダIDをメモすることで再送信時の該当フォルダ特定
  • ドライブの検索機能を使った「フォルダの使い回し&二重処理ガード」

これらを組み合わせることで、ユーザーが何度回答を修正しても、常に最新かつ綺麗な状態でファイルが整理整頓される仕組みを作ることができます。

今回の成果コード

同時送信で受付番号が重複しないよう、LockService も追加しました。

////////////
//メイン関数
////////////

function form_edited(e) {
  const lock = LockService.getScriptLock();
  try {
      lock.waitLock(30000);
      const ss = SpreadsheetApp.getActiveSpreadsheet();
      const sheet = e.range.getSheet();  // フォーム回答が入力されたシート
      const row = e.range.getRow();      // フォーム回答が入力された行
      const column = e.values.length;    // 質問の列数を取得(イベントオブジェクトから取得)
      const sheet_uketuke = ss.getSheetByName("受付シート");
    
      // 「今書き込まれた行」の純粋な回答データと質問見出しを取得
      const formData = sheet.getRange(row, 1, 1, column).getValues();
      const headers = sheet.getRange(1, 1, 1, column).getValues()[0];
    
      // 受付番号の列位置を取得
      const column_uketuke = sheet.getRange("受付番号").getColumn();
      //★受付番号の右隣を「フォルダID保存列」とする
      const column_folderId = column_uketuke + 1; 
    
      // ★すでに受付番号があるか確認(空なら新規、あれば修正)
      const existingId = sheet.getRange(row, column_uketuke).getValue();
      
      let currentId = "";
      let mainFolder = null;
      let targetRow = null;
   
      const parentFolder = DriveApp.getFolderById("******"); // <親フォルダのIDに書き換えてください
      
      //★新規か修正か条件分岐
      if (!existingId) {
        //// 【新規送信の処理】 ////
        // プロパティサービスから新しい受付番号を発行
        const lastId = Number(PropertiesService.getScriptProperties().getProperty("RECEIPT_ID"));
        currentId = lastId + 1;
        PropertiesService.getScriptProperties().setProperty("RECEIPT_ID", String(currentId));
    
        // 回答シートに受付番号を書き込み
        sheet.getRange(row, column_uketuke).setValue(currentId);    
       
        const nameIndex = headers.indexOf("あなたのお名前"); //<実際の質問名を入れてください
        //名前が入力されていればその名前、入力されていなければ「名無し」
        const userName = (nameIndex !== -1 && formData[0][nameIndex]) ? formData[0][nameIndex] : "名無し";
        
        // 今回の専用メインフォルダを作成
        const mainFolderName = "受付番号_" + currentId + "_" + userName;
        mainFolder = parentFolder.createFolder(mainFolderName);
    
        // 作成した親フォルダのIDを、受付番号の右隣にメモしておく
        sheet.getRange(row, column_folderId).setValue(mainFolder.getId());
    
        // 受付シートの末尾に新規追加し、行番号を確定
        targetRow = sheet_uketuke.getLastRow() + 1;
        sheet_uketuke.getRange(targetRow, 1).setValue(currentId);
    
      } else {
        //// 【修正送信の処理】 ////
        currentId = existingId;
    
        // 右隣のセルから、初回にメモしたフォルダIDを呼び出してフォルダを特定
        const savedFolderId = sheet.getRange(row, column_folderId).getValue();
        try {
          mainFolder = DriveApp.getFolderById(savedFolderId);
        } catch(err) {
          // 万が一フォルダが削除されていた場合のセーフティ
          mainFolder = parentFolder.createFolder("【再作成】受付番号_" + currentId);
          sheet.getRange(row, column_folderId).setValue(mainFolder.getId());
        }
    
        // 見つかったかどうか(-1かそれ以外か)で明確に分ける
        const uketukeIds = sheet_uketuke.getRange(1, 1, sheet_uketuke.getLastRow(), 1).getValues().flat();
        const foundIndex = uketukeIds.indexOf(Number(currentId));
        
        if (foundIndex === -1) {
          // 受付シートに見つからなければ末尾に追加
          targetRow = sheet_uketuke.getLastRow() + 1;
          sheet_uketuke.getRange(targetRow, 1).setValue(currentId);
        } else {
          // 見つかった場合は該当行を特定(インデックスは0から始まるので +1)
          targetRow = foundIndex + 1;
        }
      }
    
      // 受付シートの基本情報(B, C列など)を上書き転記(新規・修正共通)
      sheet_uketuke.getRange(targetRow, 2, 1, 2).setValues([formData[0].slice(0, 2)]);
    
      // ★ファイル仕分け関数を実行(特定したメインフォルダと受付シートの行番号を渡す)
      processFiles(formData[0], currentId, headers, sheet_uketuke, targetRow, mainFolder);
  } catch (e) {
    Logger.log(e);
  } finally {
    lock.releaseLock();
  }
}

//////////////////
//ファイル仕分け関数
/////////////////

function processFiles(rowData, currentId, headers, sheet_uketuke, targetRow, mainFolder) {
  //各列をループでチェック
  for (let colIndex = 0; colIndex < rowData.length; colIndex++) {

    let cellValue = rowData[colIndex];
    //ドライブのURLが含まれている列だったら処理開始
    if (typeof cellValue === "string" && cellValue.includes("drive.google.com")) {
      let questionName = headers[colIndex];
      let subFolder = null;

      // 既存の子フォルダがあるか探す
      let subFolders = mainFolder.getFoldersByName(questionName);
      if (subFolders.hasNext()) {
        subFolder = subFolders.next();  //すでにあればその子フォルダを再利用
      } else {
        subFolder = mainFolder.createFolder(questionName);  //なければ新規作成
      }

      // 受付シートの該当セルに子フォルダへのリンクを書き込み(上書き)
      let targetColumn = colIndex + 2;
      sheet_uketuke.getRange(targetRow, targetColumn)
          .setValue(
          '=HYPERLINK("' +
          subFolder.getUrl() +
          '", "📁 ' +
          questionName +
          '")'
        );

      // URL分解(フォームの複数ファイルは「, 」で区切られているので配列に分解)
      let urls = cellValue.split(", ");

      // 子フォルダ内にすでに存在するファイルIDの一覧
      let existingFileIds = [];
      let files = subFolder.getFiles();
      while (files.hasNext()) {
        let existingFile = files.next();
        existingFileIds.push(existingFile.getId());
      }

      // 添付されたURLの数だけループ処理
      for (let urlIndex = 0; urlIndex < urls.length; urlIndex++) {
        let url = urls[urlIndex];
        let fileIdMatch = url.match(/[-\w]{25,}/);
        if (!fileIdMatch) continue;
        let fileId = fileIdMatch[0];

        // すでに子フォルダ内に存在するファイルIDならスキップ
        if (existingFileIds.includes(fileId)) {
          continue;
        }

        let file = DriveApp.getFileById(fileId);

        // 現在フォルダ内にあるファイル数をもとに枝番を決定
        let fileNumber = existingFileIds.length + 1;

        file.moveTo(subFolder);

        let originalName = file.getName();
        //拡張子取得
        let extension =
          originalName.includes(".")
            ? originalName.substring(originalName.lastIndexOf("."))
            : "";
        //新しい名前=受付番号_質問名_ファイル番号_拡張子
        let newName =
          currentId +
          "_" +
          questionName +
          "_" +
          fileNumber +
          extension;

        file.setName(newName);

        // 配列更新
        existingFileIds.push(fileId);
      }
    }
  }
}

最後までお読みいただきありがとうございました!

単発の送信だけでなく、「修正送信」や「複数同時送信(LockService)」まで考慮したシステムにすることで、実務も使いやすいツールに仕上がりました。

フォーム側にも仕分け先にも負担をかけない「ショートカット活用技」なども交えつつ、ぜひご自身の環境に合わせてカスタマイズしてみてください。

image.png

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?