9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS S3の一括ダウンロードができるボタンを生やすchrome拡張を作った

Posted at

概要・成果物

普段S3をよく利用していますが、オブジェクトをまとめてダウンロードする機能がマネジメントコンソールにはなく、CLIを併用する必要があり手間に感じていました。

そこで、選択したオブジェクトを一括でダウンロードできるボタンを追加するブラウザ拡張機能を作成しました。(なぜ標準では一括でダウンロードできないのでしょうか。。?)

image.png

コード

お気持ちはコメントに書いておきました。

やっていることは至ってシンプルで以下の2種類です。

  • チェックボックスが入っているオブジェクトの情報を取得
  • 1個1個チェックボックスを取り外ししてダウンロードしていく

主要な処理はbulkButtonsとonClickBulkProcessの2つの関数に集約しています。

const bulkButtons = () => {
  // ダウンロードボタンや開くボタンなどlist-objectsが表示されている画面で表示されるtoolbarのclassを取得
  const toolbar = document.querySelector<HTMLDivElement>(
    'div[data-testid="object-list-table-action-buttons"]',
  );

  // すでにDownloadボタンを表示しているか
  const existingDownload = document.getElementById("bulk-download-btn");

  // awsのダウンロードボタン
  const awsDownLoadButton = document.querySelector<HTMLButtonElement>(
    "#download-object-button",
  );

  const configs = [
    {
      existing: existingDownload,
      awsButton: awsDownLoadButton,
      id: "bulk-download-btn",
      text: "一括ダウンロード",
    },
  ];

  configs.forEach(({ existing, awsButton, id, text }) => {
    if (toolbar && awsButton) {
      // 今現在一括ダウンロードボタンが存在していなくてtoolbar, downloadボタンがある場合はボタンを追加
      if (!existing) {
        const btn = document.createElement("button");
        btn.id = id;
        btn.textContent = text;
        btn.className =
          "copy-button copy-object-url-button awsui_button_vjswe_1379u_157 awsui_variant-normal_vjswe_1379u_205";
        btn.style.marginLeft = "6px";
        btn.addEventListener("click", () => onClickBulkProcess(awsButton));
        toolbar.appendChild(btn);
      }
    } else if (existing) {
      // 存在していないページではボタンをremove
      existing.remove();
    }
  });

  // ボタンのdisable, ableの制御
  updateBulkButtonsDisabledState();
};

const onClickBulkProcess = async (button: HTMLButtonElement) => {
  const allCheckboxes = getCurrentCheckBoxStatus();
  const preCheckBoxStatus = allCheckboxes.map((item) => item.checked);

  // 最初に1個1個チェックを外しておく
  allCheckboxes.forEach((cb) => {
    if (cb.checked) cb.click();
  });

  for (let i = 0; i < allCheckboxes.length; i++) {
    if (!preCheckBoxStatus[i]) continue;
    // チェックされている物を1個ずつ選択してダウンロードボタンをクリック
    allCheckboxes[i].click();
    button.click();
    await new Promise((r) => setTimeout(r, 200));
    // チェックボックスを外す
    allCheckboxes[i].click();
  }

  // 最初の状態に戻す
  for (let i = 0; i < allCheckboxes.length; i++) {
    if (preCheckBoxStatus[i]) {
      allCheckboxes[i].click();
    }
  }
};

const updateBulkButtonsDisabledState = () => {
  // チェックボックスに1つ以上checkがついていたらdisableをとるようにする
  const anyChecked = getCurrentCheckBoxStatus().some((cb) => cb.checked);
  ["bulk-download-btn"].forEach((id) => {
    const btn = document.getElementById(id);
    if (btn) {
      btn.classList.toggle("awsui_disabled_vjswe_1379u_223", !anyChecked);
    }
  });
};

// 現在のチェックボックスリストを取得
const getCurrentCheckBoxStatus = (): HTMLInputElement[] =>
  Array.from(
    document.querySelectorAll<HTMLInputElement>(
      'input[type="checkbox"][class^="awsui_native-input"]',
    ),
  );

// 画面が変わったときにbuikButtonsを実行できるように監視
const observer = new MutationObserver(() => {
  bulkButtons();
});

observer.observe(document.body, { childList: true, subtree: true });

// 初期実行
bulkButtons();

ソースコード

ソースコードは以下です。
chromeに公開しようとしたら登録で登録料に5$かかると言われたのでケチってやめました。

注意事項

DOM操作で1つずつダウンロードボタンをクリックしているだけのため、フォルダ構造の一括ダウンロードには対応していません。

最後に

個人的によくファイルをまとめて取得するため、作業効率が向上しました。
どなたかのお役に立てれば幸いです。

9
0
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
9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?