Compare commits

..

No commits in common. "3a1b88493f680c93a683d36256e0f0b0a763f52e" and "4ff8259c17b2c4508e84e92d30cf892c235d8008" have entirely different histories.

17 changed files with 34 additions and 1446 deletions

3
.gitignore vendored
View File

@ -133,5 +133,4 @@ dist
csv_output/ csv_output/
output/ output/
output.* output.*
docments/ docments/
keys/

View File

@ -1,17 +1,6 @@
# [Node.js]ファイルをZIP圧縮してダウンロードする方法 # [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)](#zipダウンロードサーバを構築するgcloud)
## 圧縮処理について ## 圧縮処理について
* 方法1:フロンサイドで圧縮データを処理しファイルを保存する(JSZip) * 方法1:フロンサイドで圧縮データを処理しファイルを保存する(JSZip)
@ -124,7 +113,7 @@ fetch("https://example.com/sample.zip")
``` ```
#### gcloudにあるファイルをzip形式でファイルを出力するサンプルソース #### Gloudにあるファイルをzip形式でファイルを出力するサンプルソース
(スクリプト) (スクリプト)
```js ```js
@ -165,53 +154,3 @@ async function downloadAndZip() {
downloadAndZip().catch(console.error); downloadAndZip().catch(console.error);
``` ```
#### Zipダウンロードサーバを構築する(gcloud)
(サーバ)
```js
/**
* @fileoverview Google Cloud Storage (GCS) download module.
*/
const { Storage } = require('@google-cloud/storage');
const archiver = require('archiver');
const KEY_FILE_PATH = './keys/service-account.json'
const storage = new Storage({
keyFilename: KEY_FILE_PATH});
// Load environment variables
require('dotenv').config();
// バケット名を.envから取得する
const BUCKET_NAME = process.env.BUCKET_NAME;
console.log(`BUCKET_NAME: ${BUCKET_NAME}`);
/**
* GCStorageからファイルをダウンロードする
*
* @param {http.IncomingMessage} req
* @param {http.ServerResponse} res
*/
const downloadFilesFromGCS = async (req, res) => {
// バケットからファイル一覧を取得する
const [files] = await storage.bucket(BUCKET_NAME).getFiles();
const filesToZip = files.map((file) => file.name);
res.setHeader('Content-Disposition', 'attachment; filename="files.zip"');
res.setHeader('Content-Type', 'application/zip');
const archive = archiver('zip', { zlib: { level: 9 } });
archive.on('error', (err) => res.status(500).send({ error: err.message }));
archive.pipe(res);
for (const fileName of filesToZip) {
const file = storage.bucket(BUCKET_NAME).file(fileName);
archive.append(file.createReadStream(), { name: fileName });
}
archive.finalize();
};
module.exports = downloadFilesFromGCS;
```

View File

@ -1,138 +0,0 @@
# [GCP][GCS]Node.jsライブラリでストレージを活用する
Node.jsのライブラリを使いストレージを制御します
## GCSとの連携について
セキュリティやアクセス制御の観点から、
フロントエンドは直接Cloud Storageにアクセスせず
サーバー経由で取得するのが一般的です
ただし静的なファイルや公開データであれば
フロントエンドから直接Cloud Storageにアクセスすることもあります
## ライブラリのインストール
```sh
npm install --save @google-cloud/storage
```
## GCS接続
```js
const { Storage } = require('@google-cloud/storage');
```
**サービスアカウントを環境変数で設定する場合**
```sh
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
```
```js
const storage = new Storage()
```
**JSONキーファイルをソースで読み込む場合**
```js
const KEY_FILE_PATH = './keys/service-account.json'
const storage = new Storage({
keyFilename: KEY_FILE_PATH});
```
**JSONキーファイルを使わず直接設定**
```js
const serviceAccount = {
type: "service_account",
project_id: "your-project-id",
private_key_id: "your-private-key-id",
private_key: "-----BEGIN PRIVATE KEY-----\nYOUR-PRIVATE-KEY\n-----END PRIVATE KEY-----\n",
client_email: "your-service-account@your-project-id.iam.gserviceaccount.com",
client_id: "your-client-id",
auth_uri: "https://accounts.google.com/o/oauth2/auth",
token_uri: "https://oauth2.googleapis.com/token",
auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs",
client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/your-service-account"
};
const storage = new Storage({ credentials: serviceAccount });
```
## 権限について
* ストレージ管理者
*
* 環境とストレージ オブジェクト閲覧者
* Storage オブジェクト閲覧者
* ストレージ フォルダ管理者
## サンプルコード
### バケット一覧を取得する
```js
/**
* バケット一覧を取得する
*/
async function listBuckets() {
const [buckets] = await storage.getBuckets();
console.log('Buckets:');
buckets.forEach(bucket => {
console.log(bucket.name);
});
}
```
ファイル検索については以下のページを参照してください
[[GCP]Google Cloud Storageでパターンにマッチしたファイル一覧を取得する](https://wiki.pglikers.com/en/private/cloud/gcp/gcs/glob)
### バケットの中のファイル一覧を取得する
```js
async function listFiles(bucketName) {
const [files] = await storage.bucket(bucketName).getFiles();
console.log(`Files in ${bucketName}:`);
files.forEach(file => {
console.log(file.name);
});
}
```
### 署名付きURLを発行する
```js
/**
* 署名付きURLを生成する
*/
const generateV4ReadSignedUrl = async (bucketName, fileName) => {
const options = {
version: 'v4',
action: 'read',
expires: Date.now() + 15 * 60 * 1000, // 15 minutes
};
const [url] = await storage.bucket(bucketName).file(fileName).getSignedUrl(options);
console.log(`Generated GET signed URL:`);
console.log(url);
return url;
}
```
### ファイルをダウンロードする
```js
// ファイルのコンテンツをダウンロードする
const downloadFile = async () => {
const options = {
destination: path.join(__dirname, FILE_NAME)
};
await storage.bucket(BUCKET_NAME).file(FILE_NAME).download(options);
// 取得したファイルのコンテンツからファイルを生成する
console.log(`Downloaded ${FILE_NAME}`);
};
```

View File

@ -1,9 +1,6 @@
# [Node.js]NodeでHttpサーバーを起動及び構築する方法 ## NodeでHttpサーバーを起動する方法
## `http-server`を使う場合 ### `http-server`を使う
http-serverはNode.js のシンプルな静的ファイルサーバーです。
インストールすれば簡単にローカルでファイルをホスティングできます。
```sh ```sh
# http-server をグローバルインストール(初回のみ) # http-server をグローバルインストール(初回のみ)
@ -15,72 +12,4 @@ cd src/front
# サーバー起動(デフォルトは http://localhost:8080 # サーバー起動(デフォルトは http://localhost:8080
http-server http-server
# http-server -p 3000 # http-server -p 3000
``` ```
## `http`モジュールを使う場合
* Node.jsの標準機能なので追加ライブラリなしで動作
* 軽量でシンプルな HTTPサーバーをすぐ作れる
* ルーティング機能がなく、URLごとに手動で条件分岐が必要
* ミドルウェア機能(ログ・認証・エラーハンドリング)がない
* 中~大規模開発には向かない(Expressのほうが便利)
```js
const http = require('http'); // httpモジュールをインポート
// サーバーを作成
const httpserver = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' }); // HTTPステータス 200 とヘッダーを設定
res.end('Hello, Node.js Server!'); // クライアントへレスポンスを送信
});
// ポート3000でサーバーを起動
httpserver.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});
```
## `Express`を使う場合
xpressはNode.jsの主要なWebフレームワークで
APIサーバーや動的ページの生成にも対応できます。
### インストール方法
```sh
# プロジェクトフォルダを作成して移動
mkdir sample-express-app && cd sample-express-app
# npm 初期化
npm init -y
# Express インストール
npm install express
# `server.js` を作成して、以下を記述
node server.js
```
### サーバースクリプト
`server.js`を作成する
```js
// server.js
const express = require('express');
const app = express();
const port = 3000;
// 静的ファイルを提供
app.use(express.static('public'));
// ルートエンドポイント
app.get('/', (req, res) => {
res.send('Hello, Express!');
});
// サーバー起動
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
```

View File

878
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,21 +2,18 @@
"name": "js-common-code", "name": "js-common-code",
"version": "1.0.0", "version": "1.0.0",
"description": "Javascriptで活用できるコードを開発する。 Node.jsによりサーバーサイドも作成する", "description": "Javascriptで活用できるコードを開発する。 Node.jsによりサーバーサイドも作成する",
"main": "src/server/index.js", "main": "src/server/hellp.js",
"directories": { "directories": {
"doc": "docs" "doc": "docs"
}, },
"scripts": { "scripts": {
"dev": "node src/server/index.js",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@google-cloud/storage": "^7.15.2",
"archiver": "^7.0.1", "archiver": "^7.0.1",
"csv-writer": "^1.6.0", "csv-writer": "^1.6.0"
"dotenv": "^16.4.7"
}, },
"devDependencies": { "devDependencies": {
"jsdoc": "^4.0.4" "jsdoc": "^4.0.4"

Binary file not shown.

View File

@ -1,72 +0,0 @@
/**
* @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;
}
});

View File

@ -11,11 +11,6 @@
<p >Zip File Download</p> <p >Zip File Download</p>
<button id="download-button">Zip Download(From Content)</button> <button id="download-button">Zip Download(From Content)</button>
<button id="download-button-file">Zip Download(From File)</button> <button id="download-button-file">Zip Download(From File)</button>
<p >Server </p>
<button id="download-button-server">Zip Download(From Server)</button>
<button id="download-button-upload">Zip Download(upload)</button>
<p >Google Storage Link</p>
<script src="assets/download.js"></script> <script src="assets/download.js"></script>
<script src="assets/downloadFromServer.js"></script>
</body> </body>
</html> </html>

View File

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<title>Google Cloud Storage</title>
</head>
<body>
<h1>JavaScript Develop Download To Google Cloud Storage</h1>
<p >Zip File Download</p>
<button id="download-button">Zip Download(GCS)</button>
<script src="assets/cloud-storage.js"></script>
</body>
</html>

View File

@ -1,26 +0,0 @@
/**
* @fileoverview Google Cloud Storage (GCS) download module.
*/
const path = require('path');
const { Storage } = require('@google-cloud/storage');
const KEY_FILE_PATH = './keys/service-account.json'
// バケット名、ファイル名を.envから取得する
const BUCKET_NAME = process.env.BUCKET_NAME;
const FILE_NAME = process.env.FILE_NAME;
const storage = new Storage({
keyFilename: KEY_FILE_PATH});
// ファイルのコンテンツをダウンロードする
const downloadFile = async () => {
const options = {
destination: path.join(__dirname, FILE_NAME)
};
await storage.bucket(BUCKET_NAME).file(FILE_NAME).download(options);
// 取得したファイルのコンテンツからファイルを生成する
console.log(`Downloaded ${FILE_NAME}`);
};
downloadFile();

22
src/server/archive01.js Normal file
View File

@ -0,0 +1,22 @@
/**
* 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}`));

View File

@ -1,93 +0,0 @@
const archiver = require('archiver');
const fs = require('fs');
const path = require('path');
/**
* サーバサイドで圧縮処理しフロントエンドにファイルコンテンツを返す
* @param {http.IncomingMessage} req
* @param {http.ServerResponse} res
* @returns {void}
*/
const downloadContents = (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 = {
downloadContents,
generateZipUrl
};

View File

@ -1,46 +0,0 @@
/**
* @fileoverview Google Cloud Storage (GCS) download module.
*/
const { Storage } = require('@google-cloud/storage');
const archiver = require('archiver');
const KEY_FILE_PATH = './keys/service-account.json'
const storage = new Storage({
keyFilename: KEY_FILE_PATH});
// Load environment variables
require('dotenv').config();
// バケット名を.envから取得する
const BUCKET_NAME = process.env.BUCKET_NAME;
console.log(`BUCKET_NAME: ${BUCKET_NAME}`);
/**
* GCStorageからファイルをダウンロードする
*
* @param {http.IncomingMessage} req
* @param {http.ServerResponse} res
*/
const downloadFilesFromGCS = async (req, res) => {
// バケットからファイル一覧を取得する
const [files] = await storage.bucket(BUCKET_NAME).getFiles();
const filesToZip = files.map((file) => file.name);
res.setHeader('Content-Disposition', 'attachment; filename="files.zip"');
res.setHeader('Content-Type', 'application/zip');
const archive = archiver('zip', { zlib: { level: 9 } });
archive.on('error', (err) => res.status(500).send({ error: err.message }));
archive.pipe(res);
for (const fileName of filesToZip) {
const file = storage.bucket(BUCKET_NAME).file(fileName);
archive.append(file.createReadStream(), { name: fileName });
}
archive.finalize();
};
module.exports = downloadFilesFromGCS;

View File

@ -1,39 +1,12 @@
const http = require('http'); const http = require('http');
const { downloadContents, generateZipUrl } = require('./download/download');
const downloadFilesFromGCS = require('./download/gcsArchive');
/**
* Create a new HTTP server
*/
const server = http.createServer((req, res) => { const server = http.createServer((req, res) => {
// Set the response HTTP header with HTTP status and Content type res.statusCode = 200;
// root path res.setHeader('Content-Type', 'text/plain');
if (req.url === '/') { res.end('Hello World\n');
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World\n');
} else if (req.url === '/downdload') {
// ZIPファイルのコンテンツをそのまま返す
downloadContents(req, res);
} else if (req.url === '/generate-zip') {
// ZIPファイルの生成してストレージのURLを返す
generateZipUrl(req, res);
} else if (req.url === '/downdload-gcs') {
// GCSからZIPファイルのコンテンツをそのまま返す
downloadFilesFromGCS(req, res);
} else {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Not Found\n');
}
}); });
const port = 3000; const port = 3000;
server.listen(port, () => { server.listen(port, () => {
console.log(`Server running at http://localhost:${port}/`); console.log(`Server running at http://localhost:${port}/`);
}); });