157 lines
5.2 KiB
Markdown
157 lines
5.2 KiB
Markdown
|
|
# [Node.js]ファイルをZIP圧縮してダウンロードする方法
|
|
|
|
## 圧縮処理について
|
|
|
|
* 方法1:フロンサイドで圧縮データを処理しファイルを保存する(JSZip)
|
|
* 方法2:サーバサイドで圧縮処理しファイルを出力する
|
|
* 方法3:サーバサイドで圧縮処理しフロントでBlobによりファイルを取得する
|
|
|
|
### 方法1:フロンサイドで圧縮データを処理しファイルを保存する(JSZip)
|
|
|
|
* **ファイル量が多い場合(30MB以上など)はサーバーサイドで処理するほうがいい**
|
|
* 大容量データの処理には向かない
|
|
* ユーザーのデバイスの性能に依存する
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
User->>Front: ダウンロードボタンを要求する
|
|
loop ファイル毎に処理する
|
|
Front->>Storage: ファイルを要求する(fetch)
|
|
Storage-->>Front: ファイル情報を返す
|
|
end
|
|
Front-->>Front: Zip処理を実行する(JSZip)
|
|
Front-->>User: ダウンロード処理を実行する(blob)
|
|
```
|
|
|
|
* https://stuk.github.io/jszip/documentation/examples.html
|
|
* [サンプルコード](../src/front/assets/download.js)
|
|
|
|
```
|
|
import JSZip from "jszip";
|
|
```
|
|
|
|
**CDNでインポートする場合**
|
|
|
|
```js
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
|
```
|
|
|
|
---
|
|
|
|
### 方法2:サーバサイドで圧縮処理しファイルを出力する
|
|
|
|
サーバーサイドでarchiverを使ってZIPを作成し、
|
|
一時ファイルとして保存してからダウンロード用のURLを返します
|
|
|
|
* archiverモジュールで実行する
|
|
* Zipファイルの保存のためにストレージが別で必要
|
|
* ライフサイクルを考慮しないと不要なファイルが溜まる
|
|
* 大容量データの圧縮に適してい
|
|
* 複数のリクエストに対応しやすい
|
|
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
User->>Front: ダウンロードボタンを要求する
|
|
Front->>Server: ダウンロードを要求する
|
|
Server->>Storage: ファイルデータ取得API
|
|
Storage->>Server: レスポンス
|
|
Server->>Server: Zip処理を実行する(archiver)
|
|
Server->>Storage: 作成した Zipを保存する
|
|
Storage->>Server: レスポンス
|
|
Server-->>Front: ファイルのURLを返す
|
|
Front->>Storage: URLをリンクする(HTTP)
|
|
Storage-->>User: ファイルをダウンロードする
|
|
```
|
|
|
|
```sh
|
|
npm install archiver
|
|
```
|
|
---
|
|
|
|
### 方法3:サーバサイドで圧縮処理しフロントでBlobによりファイルを取得する
|
|
|
|
ZIPを一時ファイルとして保存せずに
|
|
直接フロントエンドにストリームとして送信します。
|
|
|
|
* archiverモジュールで実行する
|
|
* 保存せずにファイルコンテンツを返す
|
|
* リアルタイムでファイルをストリーミング可能
|
|
* ストレージの管理が不要
|
|
* クライアントがダウンロードを途中でキャンセルするとデータが失われる可能性がある
|
|
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
User->>Front: ダウンロードボタンを要求する
|
|
Front->>Server: ダウンロードを要求する
|
|
Server->>Storage: ファイルデータ取得API
|
|
Storage->>Server: レスポンス
|
|
Server->>Server: Zip処理を実行する(archiver)
|
|
Storage->>Front: Zipされたファイルコンテンツを返す
|
|
Front-->>User: ダウンロード処理を実行する(blob)
|
|
```
|
|
|
|
## サンプルソース
|
|
|
|
### Blobを使用してZIPファイルを処理する(フロント側の処理)**
|
|
|
|
```js
|
|
fetch("https://example.com/sample.zip")
|
|
.then(response => response.blob())
|
|
.then(blob => {
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = "downloaded.zip";
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
})
|
|
.catch(error => console.error("Error downloading ZIP:", error));
|
|
```
|
|
|
|
|
|
#### Gloudにあるファイルをzip形式でファイルを出力するサンプルソース
|
|
(スクリプト)
|
|
|
|
```js
|
|
const { Storage } = require('@google-cloud/storage');
|
|
const archiver = require('archiver');
|
|
const fs = require('fs');
|
|
|
|
const storage = new Storage();
|
|
const bucketName = 'your-bucket-name';
|
|
const filesToDownload = ['file1.csv', 'file2.csv']; // 圧縮したいファイルリスト
|
|
const outputZip = 'output.zip';
|
|
|
|
async function downloadAndZip() {
|
|
const output = fs.createWriteStream(outputZip);
|
|
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
|
|
output.on('close', () => {
|
|
console.log(`Zip file created: ${outputZip} (${archive.pointer()} bytes)`);
|
|
});
|
|
|
|
archive.pipe(output);
|
|
|
|
for (const fileName of filesToDownload) {
|
|
const file = storage.bucket(bucketName).file(fileName);
|
|
const [exists] = await file.exists();
|
|
|
|
if (exists) {
|
|
console.log(`Adding ${fileName} to archive...`);
|
|
archive.append(file.createReadStream(), { name: fileName });
|
|
} else {
|
|
console.warn(`File not found: ${fileName}`);
|
|
}
|
|
}
|
|
|
|
archive.finalize();
|
|
}
|
|
|
|
downloadAndZip().catch(console.error);
|
|
```
|
|
|