From 37ba3e8532ba4f5c4b6ee2ff90a810fd446776c1 Mon Sep 17 00:00:00 2001 From: "ry.yamafuji" Date: Fri, 21 Mar 2025 00:57:41 +0900 Subject: [PATCH] =?UTF-8?q?archive=E7=94=A8=E3=81=AE=E5=87=A6=E7=90=86?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/archive.md | 56 +++++++++++++++- src/front/assets/downloadFromServer.js | 72 ++++++++++++++++++++ src/front/download.html | 4 ++ src/server/archive01.js | 22 ------- src/server/download/download.js | 91 ++++++++++++++++++++++++++ src/server/index.js | 29 +++++++- 6 files changed, 248 insertions(+), 26 deletions(-) create mode 100644 src/front/assets/downloadFromServer.js delete mode 100644 src/server/archive01.js create mode 100644 src/server/download/download.js diff --git a/docs/archive.md b/docs/archive.md index bc4d9e0..3facd59 100644 --- a/docs/archive.md +++ b/docs/archive.md @@ -1,6 +1,17 @@ # [Node.js]ファイルをZIP圧縮してダウンロードする方法 +- [\[Node.js\]ファイルをZIP圧縮してダウンロードする方法](#nodejsファイルをzip圧縮してダウンロードする方法) + - [圧縮処理について](#圧縮処理について) + - [方法1:フロンサイドで圧縮データを処理しファイルを保存する(JSZip)](#方法1フロンサイドで圧縮データを処理しファイルを保存するjszip) + - [方法2:サーバサイドで圧縮処理しファイルを出力する](#方法2サーバサイドで圧縮処理しファイルを出力する) + - [方法3:サーバサイドで圧縮処理しフロントでBlobによりファイルを取得する](#方法3サーバサイドで圧縮処理しフロントでblobによりファイルを取得する) + - [サンプルソース](#サンプルソース) + - [Blobを使用してZIPファイルを処理する(フロント側の処理)\*\*](#blobを使用してzipファイルを処理するフロント側の処理) + - [gcloudにあるファイルをzip形式でファイルを出力するサンプルソース](#gcloudにあるファイルをzip形式でファイルを出力するサンプルソース) + - [Zipダウンロードサーバを構築する(gcloud + express)](#zipダウンロードサーバを構築するgcloud--express) + + ## 圧縮処理について * 方法1:フロンサイドで圧縮データを処理しファイルを保存する(JSZip) @@ -113,7 +124,7 @@ fetch("https://example.com/sample.zip") ``` -#### Gloudにあるファイルをzip形式でファイルを出力するサンプルソース +#### gcloudにあるファイルをzip形式でファイルを出力するサンプルソース (スクリプト) ```js @@ -154,3 +165,46 @@ async function downloadAndZip() { downloadAndZip().catch(console.error); ``` +#### Zipダウンロードサーバを構築する(gcloud + express) +(サーバ) + +```js +const { Storage } = require('@google-cloud/storage'); +const express = require('express'); +const archiver = require('archiver'); + +const storage = new Storage(); +const bucketName = 'your-bucket-name'; + +const app = express(); +const port = 3000; + +app.get('/download-zip', async (req, res) => { + const filesToDownload = ['file1.csv', 'file2.csv']; // 圧縮したいファイルリスト + + res.setHeader('Content-Type', 'application/zip'); + res.setHeader('Content-Disposition', 'attachment; filename="compressed_files.zip"'); + + const archive = archiver('zip', { zlib: { level: 9 } }); + archive.pipe(res); // レスポンスに直接圧縮データを送る + + 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(); // ZIP 圧縮を完了 +}); + +app.listen(port, () => { + console.log(`Server running at http://localhost:${port}`); +}); +``` + diff --git a/src/front/assets/downloadFromServer.js b/src/front/assets/downloadFromServer.js new file mode 100644 index 0000000..177194c --- /dev/null +++ b/src/front/assets/downloadFromServer.js @@ -0,0 +1,72 @@ +/** + * @description downloadボタンを押下したらCSVファイルをzip圧縮してダウンロードする + */ + + +// ダウンロードボタン +const downloadButtonServer = document.getElementById('download-button-server'); + +// ダウンロードボタンを押下した時の処理を記載する +downloadButtonServer.addEventListener('click', async () => { + // Blobで取得する方法 + console.log('downloadButtonServer'); + // ダウンロードボタンを無効化する + downloadButtonServer.disabled = true; + // ダウンロード処理を実行する + try { + // localhost:3000/downdload にリクエストを送る + const response = await fetch('http://localhost:3000/downdload'); + if (!response.ok) throw new Error('Failed to fetch'); + + // ZIPファイルを取得 + const zipBlob = await response.blob(); + const a = document.createElement("a"); + a.href = URL.createObjectURL(zipBlob); + a.download = "serverFile.zip"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + } catch (e) { + // エラーが発生した場合 + console.error(e); + alert('ダウンロードに失敗しました'); + } finally { + // ダウンロードボタンを有効化する + downloadButtonServer.disabled = false; + } +}); + +// ダウンロードボタン(アップロード) +const downloadButtonUpload = document.getElementById('download-button-upload'); + +// ダウンロードボタンを押下した時の処理を記載する +downloadButtonUpload.addEventListener('click', async () => { + console.log('downloadButtonUpload'); + // ダウンロードボタンを無効化する + downloadButtonUpload.disabled = true; + // サーバーにアップロード処理APIを送信する + try { + // localhost:3000/generate-zip にリクエストを送る + const response = await fetch('http://localhost:3000/generate-zip'); + if (!response.ok) throw new Error('Failed to fetch'); + + // レスポンスからURLを取得 + const { url } = await response.json(); + // 取得したURLを開く + window.open(url); + + } catch (e) { + // エラーが発生した場合 + console.error(e); + alert('ダウンロードに失敗しました'); + } finally { + // ダウンロードボタンを有効化する + downloadButtonUpload.disabled = false; + } + +}); + + + + + diff --git a/src/front/download.html b/src/front/download.html index 9b8ffd5..778f801 100644 --- a/src/front/download.html +++ b/src/front/download.html @@ -11,6 +11,10 @@

Zip File Download

+

Server

+ + + \ No newline at end of file diff --git a/src/server/archive01.js b/src/server/archive01.js deleted file mode 100644 index 0250be9..0000000 --- a/src/server/archive01.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * ZIPファイルを作成してダウンロードサーバ - * - * @description - * - ZIPファイルを作成してダウンロードするサーバー - * - Node.js + Express + archiver - */ - -const express = require("express"); -const fs = require("fs"); -const path = require("path"); -const archiver = require("archiver"); - -const app = express(); -const PORT = 3000; - -app.get("/download-zip", async (req, res) => { -}); - -// ZIPのダウンロード用エンドポイント -app.use(express.static(__dirname)); -app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`)); \ No newline at end of file diff --git a/src/server/download/download.js b/src/server/download/download.js new file mode 100644 index 0000000..636b386 --- /dev/null +++ b/src/server/download/download.js @@ -0,0 +1,91 @@ +const archiver = require('archiver'); +const fs = require('fs'); +const path = require('path'); + +/** + * サーバサイドで圧縮処理しフロントエンドにファイルコンテンツを返す + * @param {http.IncomingMessage} req + * @param {http.ServerResponse} res + * @returns {void} + */ +const downdloadContents = (req, res) => { + + // ファイルの圧縮処理 + // CORSヘッダーを追加 + res.setHeader('Access-Control-Allow-Origin', '*'); // すべてのオリジンを許可 + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); + // CSVファイル対象 + const csvFilePaths = [ + "./src/front/assets/data/test_data_1.csv", + "./src/front/assets/data/test_data_2.csv" + ] + res.setHeader('Content-Type', 'application/zip'); + res.setHeader('Content-Disposition', 'attachment; filename=test.zip'); + + const archive = archiver('zip', { zlib: { level: 9 } }); + + archive.on('error', (err) => { + console.error('Archiver Error:', err); + res.statusCode = 500; + res.end('Internal Server Error'); + }); + + // レスポンスに直接圧縮データを送る + archive.pipe(res); + + // ZIPにファイルを追加 + csvFilePaths.forEach((filePath) => { + if (fs.existsSync(filePath)) { + archive.append(fs.createReadStream(filePath), { name: path.basename(filePath) }); + } else { + console.warn(`File not found: ${filePath}`); + } + }); + // ZIPファイルを作成 + archive.finalize(); +} + +/** + * generateZipUrl + * + * @param {http.IncomingMessage} req + * @param {http.ServerResponse} res + */ +const generateZipUrl = (req, res) => { + // CORSヘッダーを追加 + res.setHeader('Access-Control-Allow-Origin', '*'); // すべてのオリジンを許可 + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); + res.setHeader('Content-Type', 'application/json'); + // CSVファイル対象 + const csvFilePaths = [ + "./src/front/assets/data/test_data_1.csv", + "./src/front/assets/data/test_data_2.csv" + ] + // ZIPファイルを生成してファイルを保存する + const archive = archiver('zip', { zlib: { level: 9 } }); + const zipFilePath = './src/front/assets/data/test.zip'; + const output = fs.createWriteStream(zipFilePath); + archive.pipe(output); + + // ZIPにファイルを追加 + csvFilePaths.forEach((filePath) => { + if (fs.existsSync(filePath)) { + archive.append(fs.createReadStream(filePath), { name: path.basename(filePath) }); + } else { + console.warn(`File not found: ${filePath}`); + } + }); + + // ZIPファイルを作成 + archive.finalize(); + + // レスポンスにはURLを返す(~/assets/data/test.zip) + const url = `assets/data/test.zip`; + res.end(JSON.stringify({ url: url })); +} + + +module.exports = downdloadContents; +module.exports = generateZipUrl; diff --git a/src/server/index.js b/src/server/index.js index fd51a2d..59839cb 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -1,9 +1,32 @@ const http = require('http'); +// download/download.jsのdowndloadContentsメソッドをインポート +const downdloadContents = require('./download/download'); +// download/download.jsのgenerateZipUrlメソッドをインポート +const generateZipUrl = require('./download/download'); + +/** + * Create a new HTTP server + */ const server = http.createServer((req, res) => { - res.statusCode = 200; - res.setHeader('Content-Type', 'text/plain'); - res.end('Hello World\n'); + // Set the response HTTP header with HTTP status and Content type + // root path + if (req.url === '/') { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end('Hello World\n'); + } else if (req.url === '/downdload') { + // ZIPファイルのコンテンツをそのまま返す + downdloadContents(req, res); + } else if (req.url === '/generate-zip') { + // ZIPファイルの生成してストレージのURLを返す + generateZipUrl(req, res); + + } else { + res.statusCode = 404; + res.setHeader('Content-Type', 'text/plain'); + res.end('Not Found\n'); + } }); const port = 3000;