はじめに

ページ中の複数の画像を zip に圧縮してローカルにダウンロードする方法を整理した。
JavaScript で画像をローカルにダウンロード の続き

TL;DR

この記事が参考になった方
ここここからチャージや購入してくれると嬉しいです(ブログ主へのプレゼントではなく、ご自身へのチャージ)
欲しいもの / Wish list

目次

  1. はじめに
  2. TL;DR
  3. 環境・条件
  4. 詳細
    1. 完成形
    2. 解説
      1. jszip を CDN から読み込み: loadJSZipFromCDN
      2. ボタン追加: addImageDownloadButton
      3. 画像リクエスト: downloadImages
      4. zip 生成: generateImagesZip
  5. まとめ
  6. その他・メモ
  7. 参考文献

環境・条件

  • Google Chrome バージョン: 79.0.3945.88(Official Build) (64 ビット)
  • jszip v3.2.1 (CDN)

詳細

いらすとや トップページの『〇〇のイラスト』の画像をまとめてダウンロード」を例にダウンロードボタンを作る。

完成形

前回 と同様に、先に完成形。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// jszip を CDN から読み込み
function loadJSZipFromCDN()
{
let script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/jszip@3.2.1/dist/jszip.js";
document.head.append(script);
}
loadJSZipFromCDN();

// 一括ダウンロードボタンの追加
function addImageDownloadButton() {
document.body.insertAdjacentHTML(
"afterBegin",
`<button id="download">画像一括ダウンロード</button>`
);
document.getElementById("download").addEventListener("click", downloadImages);
}

// 画像の一括ダウンロード
async function downloadImages() {
// 画像 URL
const sources = [].slice.call(
document.querySelectorAll("#section_banner img")
).map(img => img.getAttribute("src"));

// JSZip に追加するために非同期リクエストを Promise で wrap
const imagePromises = sources.map(
(src, i) => new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('GET', src, true);
xhr.responseType = "blob";
xhr.onload = function() {
// ファイル名とデータ返却
const fileName = src.slice(src.lastIndexOf("/") + 1);
resolve({ data: this.response, fileName: fileName });
};
// reject だと await Promise.all を抜けてしまう
// => resolve でデータ無し
xhr.onerror = () => resolve({ data: null });
xhr.onabort = () => resolve({ data: null });
xhr.ontimeout = () => resolve({ data: null });
xhr.send();
})
);

// すべての画像が取得できたら zip 生成
const images = await Promise.all(imagePromises);
generateImagesZip(images);
}

// zip ファイルで画像をダウンロード
function generateImagesZip(images) {
let zip = new JSZip();

// フォルダ作成
const folderName = "いらすとや バナー";
let folder = zip.folder(folderName);

// フォルダ下に画像を格納
images.forEach(image => {
if (image.data && image.fileName) {
folder.file(image.fileName, image.data)
}
});

// zip を生成
zip.generateAsync({ type: "blob" }).then(blob => {

// ダウンロードリンクを 生成
let dlLink = document.createElement("a");

// blob から URL を生成
const dataUrl = URL.createObjectURL(blob);
dlLink.href = dataUrl;
dlLink.download = `${folderName}.zip`;

// 設置/クリック/削除
document.body.insertAdjacentElement("beforeEnd", dlLink);
dlLink.click();
dlLink.remove();

// オブジェクト URL の開放
setTimeout(function() {
window.URL.revokeObjectURL(dataUrl);
}, 1000);
});
}

addImageDownloadButton();

上記スクリプトを Dev tools のコンソールで実行すると、トップにボタンが配置される。

このボタンをクリックすると、 いらすとや バナー.zip がダウンロードされるはず。

展開すると下記のような感じ。

解説

関数単位で簡単に解説。

jszip を CDN から読み込み: loadJSZipFromCDN

1
2
3
4
5
6
7
8
// jszip を CDN から読み込み
function loadJSZipFromCDN()
{
let script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/jszip@3.2.1/dist/jszip.js";
document.head.append(script);
}
loadJSZipFromCDN();
  • headjszip 読み込みの script タグを追加
    • npm などでインストールして使用する場合は不要

ボタン追加: addImageDownloadButton

1
2
3
4
5
6
7
8
// 一括ダウンロードボタンの追加
function addImageDownloadButton() {
document.body.insertAdjacentHTML(
"afterBegin",
`<button id="download">画像一括ダウンロード</button>`
);
document.getElementById("download").addEventListener("click", downloadImages);
}
  • 前回 と同じなため省略

画像リクエスト: downloadImages

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 画像の一括ダウンロード
async function downloadImages() {
// 画像 URL
const sources = [].slice.call(
document.querySelectorAll("#section_banner img")
).map(img => img.getAttribute("src"));

// JSZip に追加するために非同期リクエストを Promise で wrap
const imagePromises = sources.map(
(src, i) => new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('GET', src, true);
xhr.responseType = "blob";
xhr.onload = function() {
// ファイル名とデータ返却
const fileName = src.slice(src.lastIndexOf("/") + 1);
resolve({ data: this.response, fileName: fileName });
};
// reject だと await Promise.all を抜けてしまう
// => resolve でデータ無し
xhr.onerror = () => resolve({ data: null });
xhr.onabort = () => resolve({ data: null });
xhr.ontimeout = () => resolve({ data: null });
xhr.send();
})
);

// すべての画像が取得できたら zip 生成
const images = await Promise.all(imagePromises);
generateImagesZip(images);
}
  • バナー画像には "#section_banner img" でアクセス可能
    • querySelectorAll の戻り値 NodeListforEachmap が使えないので、[].slice.call で変換
      • 最近だと Array.from() とか [...nodeList] みたいな感じでも良いっぽい
  • 画像がすべて取得できたら zip 生成」としたいので、リクエスト処理を new Promise で wrap
  • await Promise.all() ですべての非同期リクエストが完了するのを待つ

zip 生成: generateImagesZip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// zip ファイルで画像をダウンロード
function generateImagesZip(images) {
let zip = new JSZip();

// フォルダ作成
const folderName = "いらすとや バナー";
let folder = zip.folder(folderName);

// フォルダ下に画像を格納
images.forEach(image => {
if (image.data && image.fileName) {
folder.file(image.fileName, image.data)
}
});

// zip を生成
zip.generateAsync({ type: "blob" }).then(blob => {

// ダウンロードリンクを 生成
let dlLink = document.createElement("a");

// blob から URL を生成
const dataUrl = URL.createObjectURL(blob);
dlLink.href = dataUrl;
dlLink.download = `${folderName}.zip`;

// 設置/クリック/削除
document.body.insertAdjacentElement("beforeEnd", dlLink);
dlLink.click();
dlLink.remove();

// オブジェクト URL の開放
setTimeout(function() {
window.URL.revokeObjectURL(dataUrl);
}, 1000);
});
}
  • folder() で、フォルダの生成
  • folder.file() で、フォルダ下にファイルを配置(生成)
    • 対応フォーマットは下記
      • String
      • ArrayBuffer
      • Uint8Array
      • Buffer
      • Blob
      • Promise
      • Nodejs stream
  • generateAsync() で、zip ファイルの生成
    • typeblob で生成することで、あとの処理は前回 と同じ流れ
      • createObjectURL<a href="..." download="xxxx.zip"> → クリック&削除

まとめ

その他・メモ

参考文献

関連記事

この記事が参考になった方
ここここからチャージや購入してくれると嬉しいです(ブログ主へのプレゼントではなく、ご自身へのチャージ)
欲しいもの / Wish list