From 7febf69c0cf5dcea2f24b6095e4d61e935b57ee1 Mon Sep 17 00:00:00 2001 From: "ry.yamafuji" Date: Sat, 22 Mar 2025 09:37:34 +0900 Subject: [PATCH] =?UTF-8?q?GCS=E3=81=8B=E3=82=89=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=82=92=E5=8F=96=E5=BE=97=E3=81=97=E3=81=A6?= =?UTF-8?q?archive=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- docs/cloudStorageByNode.md | 138 ++++++++++++++++++++++++++++++ package-lock.json | 15 +++- package.json | 6 +- src/front/assets/cloud-storage.js | 0 src/front/assets/data/test.zip | Bin 0 -> 534 bytes src/front/download.html | 1 + src/front/gcs.html | 15 ++++ src/script/gcs.js | 26 ++++++ src/server/download/download.js | 8 +- src/server/download/gcsArchive.js | 46 ++++++++++ src/server/index.js | 18 ++-- 12 files changed, 262 insertions(+), 14 deletions(-) create mode 100644 docs/cloudStorageByNode.md create mode 100644 src/front/assets/cloud-storage.js create mode 100644 src/front/assets/data/test.zip create mode 100644 src/front/gcs.html create mode 100644 src/script/gcs.js create mode 100644 src/server/download/gcsArchive.js diff --git a/.gitignore b/.gitignore index 0d48790..60a6164 100644 --- a/.gitignore +++ b/.gitignore @@ -133,4 +133,5 @@ dist csv_output/ output/ output.* -docments/ \ No newline at end of file +docments/ +keys/ \ No newline at end of file diff --git a/docs/cloudStorageByNode.md b/docs/cloudStorageByNode.md new file mode 100644 index 0000000..003724c --- /dev/null +++ b/docs/cloudStorageByNode.md @@ -0,0 +1,138 @@ +# [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}`); +}; +``` diff --git a/package-lock.json b/package-lock.json index 2a59f05..00ff657 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "dependencies": { "@google-cloud/storage": "^7.15.2", "archiver": "^7.0.1", - "csv-writer": "^1.6.0" + "csv-writer": "^1.6.0", + "dotenv": "^16.4.7" }, "devDependencies": { "jsdoc": "^4.0.4" @@ -601,6 +602,18 @@ "node": ">=0.4.0" } }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/package.json b/package.json index 5a8f23f..08ffa37 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,12 @@ "name": "js-common-code", "version": "1.0.0", "description": "Javascriptで活用できるコードを開発する。 Node.jsによりサーバーサイドも作成する", - "main": "src/server/hellp.js", + "main": "src/server/index.js", "directories": { "doc": "docs" }, "scripts": { + "dev": "node src/server/index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", @@ -14,7 +15,8 @@ "dependencies": { "@google-cloud/storage": "^7.15.2", "archiver": "^7.0.1", - "csv-writer": "^1.6.0" + "csv-writer": "^1.6.0", + "dotenv": "^16.4.7" }, "devDependencies": { "jsdoc": "^4.0.4" diff --git a/src/front/assets/cloud-storage.js b/src/front/assets/cloud-storage.js new file mode 100644 index 0000000..e69de29 diff --git a/src/front/assets/data/test.zip b/src/front/assets/data/test.zip new file mode 100644 index 0000000000000000000000000000000000000000..61443308a6b25a8c271b963469920a8ebe2c9f2f GIT binary patch literal 534 zcmWIWW@Zs#-~dAI`jRLHB)|`3m!uY##HS>dB*q)+B^Q^4Uf9dU&ZE>Wj@`VdITUJb~ zU!AmNRoI4mVT+&Cy)WR{HDeBE4P$^eJBKlk^_g~{3mq957y^j&gAv>hh5`^jq?qrY z+azXIc1u>y$Zc9Ap9up-lBi9t1f4=h5j&+;v*jPN?eCpBnPq`33 z2(J{n!K=D>%_$F?DZOl08yuXQFRqX}SeIckE9COEIX!7td9<82UtARBuxqEp+Et~| zp}ke#_a9aa{d;)k0e+AV)RwRAY=!%Pkx7>s5f8||1H}UbC@?H(1hLR!CBPe1GqS;` cnvbAp1_wugH!B;+OeP?#0@7{3@MK^B0NLWX4*&oF literal 0 HcmV?d00001 diff --git a/src/front/download.html b/src/front/download.html index 778f801..8587432 100644 --- a/src/front/download.html +++ b/src/front/download.html @@ -14,6 +14,7 @@

Server

+

Google Storage Link

diff --git a/src/front/gcs.html b/src/front/gcs.html new file mode 100644 index 0000000..b7cea62 --- /dev/null +++ b/src/front/gcs.html @@ -0,0 +1,15 @@ + + + + + + + Google Cloud Storage + + +

JavaScript Develop Download To Google Cloud Storage

+

Zip File Download

+ + + + \ No newline at end of file diff --git a/src/script/gcs.js b/src/script/gcs.js new file mode 100644 index 0000000..4a62d56 --- /dev/null +++ b/src/script/gcs.js @@ -0,0 +1,26 @@ +/** + * @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(); + diff --git a/src/server/download/download.js b/src/server/download/download.js index 636b386..ead7b46 100644 --- a/src/server/download/download.js +++ b/src/server/download/download.js @@ -8,7 +8,7 @@ const path = require('path'); * @param {http.ServerResponse} res * @returns {void} */ -const downdloadContents = (req, res) => { +const downloadContents = (req, res) => { // ファイルの圧縮処理 // CORSヘッダーを追加 @@ -87,5 +87,7 @@ const generateZipUrl = (req, res) => { } -module.exports = downdloadContents; -module.exports = generateZipUrl; +module.exports = { + downloadContents, + generateZipUrl +}; diff --git a/src/server/download/gcsArchive.js b/src/server/download/gcsArchive.js new file mode 100644 index 0000000..a2199b5 --- /dev/null +++ b/src/server/download/gcsArchive.js @@ -0,0 +1,46 @@ +/** + * @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; diff --git a/src/server/index.js b/src/server/index.js index 59839cb..684ad46 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -1,8 +1,8 @@ const http = require('http'); -// download/download.jsのdowndloadContentsメソッドをインポート -const downdloadContents = require('./download/download'); -// download/download.jsのgenerateZipUrlメソッドをインポート -const generateZipUrl = require('./download/download'); + +const { downloadContents, generateZipUrl } = require('./download/download'); +const downloadFilesFromGCS = require('./download/gcsArchive'); + /** @@ -17,11 +17,13 @@ const server = http.createServer((req, res) => { res.end('Hello World\n'); } else if (req.url === '/downdload') { // ZIPファイルのコンテンツをそのまま返す - downdloadContents(req, res); + 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'); @@ -32,4 +34,6 @@ const server = http.createServer((req, res) => { const port = 3000; server.listen(port, () => { console.log(`Server running at http://localhost:${port}/`); -}); \ No newline at end of file +}); + +