Merge branch 'feature/archive'
This commit is contained in:
commit
21bd8e37da
5
.gitignore
vendored
5
.gitignore
vendored
@ -130,3 +130,8 @@ dist
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
csv_output/
|
||||
output/
|
||||
output.*
|
||||
docments/
|
||||
keys/
|
@ -1,7 +1,7 @@
|
||||
# js-common-code
|
||||
|
||||
Javascriptで活用できるコードを開発する。
|
||||
Node.jsによりサーバーサイドも作成する
|
||||
Node.jsによりサーバーサイドも作成
|
||||
|
||||
|
||||
|
||||
|
217
docs/archive.md
Normal file
217
docs/archive.md
Normal file
@ -0,0 +1,217 @@
|
||||
|
||||
# [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)
|
||||
* 方法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));
|
||||
```
|
||||
|
||||
|
||||
#### gcloudにあるファイルを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);
|
||||
```
|
||||
|
||||
#### 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;
|
||||
```
|
||||
|
138
docs/cloudStorageByNode.md
Normal file
138
docs/cloudStorageByNode.md
Normal file
@ -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}`);
|
||||
};
|
||||
```
|
56
docs/cloudStorageLifecycle.md
Normal file
56
docs/cloudStorageLifecycle.md
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
# [GCP][GCS]ファイルのライフサイクルと運用について
|
||||
|
||||
* [[GCP]Google Cloud Storageを活用する・静的ファイルをデプロイする方法](https://wiki.pglikers.com/en/private/cloud/gcp/gcs/hosting)
|
||||
|
||||
## Bucketのファイルにライフサイクルを設定する
|
||||
|
||||
- **活用できるユースケース**
|
||||
- ○○日経過にファイルを削除する
|
||||
|
||||
[公式ドキュメント](https://cloud.google.com/storage/docs/lifecycle?hl=ja)
|
||||
|
||||
### GUIでの設定
|
||||
|
||||
1. Google Cloudコンソールで、Cloud Storageの[バケット]を開く
|
||||
* https://console.cloud.google.com/storage/browser?hl=ja
|
||||
2. 対象のBucketを開く
|
||||
3. `ライフサイクル`タブを押下する
|
||||
4. ルールを追加する
|
||||
|
||||
### コマンドで設定する場合
|
||||
|
||||
#### 1. 構成ファイルを作成する
|
||||
|
||||
1日経過後に削除する場合の構成
|
||||
[(構成についてはこちら)](https://cloud.google.com/storage/docs/lifecycle-configurations?hl=ja)
|
||||
|
||||
```json
|
||||
{
|
||||
"lifecycle": {
|
||||
"rule": [
|
||||
{
|
||||
"action": { "type": "Delete" },
|
||||
"condition": {
|
||||
"age": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. ライフサイクル構成を設定する
|
||||
|
||||
```sh
|
||||
cloud storage buckets update gs://BUCKET_NAME \
|
||||
--lifecycle-file=LIFECYCLE_CONFIG_FILE
|
||||
```
|
||||
|
||||
- LIFECYCLE_CONFIG_FILEは、作成したJSONファイルのパスです
|
||||
|
||||
#### ライフサイクル構成を削除する場合のコマンド
|
||||
|
||||
```sh
|
||||
gcloud storage buckets update gs://BUCKET_NAME --clear-lifecycle
|
||||
```
|
86
docs/httpserver.md
Normal file
86
docs/httpserver.md
Normal file
@ -0,0 +1,86 @@
|
||||
# [Node.js]NodeでHttpサーバーを起動及び構築する方法
|
||||
|
||||
## `http-server`を使う場合
|
||||
|
||||
http-serverはNode.js のシンプルな静的ファイルサーバーです。
|
||||
インストールすれば簡単にローカルでファイルをホスティングできます。
|
||||
|
||||
```sh
|
||||
# http-server をグローバルインストール(初回のみ)
|
||||
npm install -g http-server
|
||||
|
||||
# ファイルがあるディレクトリへ移動
|
||||
cd src/front
|
||||
|
||||
# サーバー起動(デフォルトは http://localhost:8080)
|
||||
http-server
|
||||
# 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}`);
|
||||
});
|
||||
```
|
130
docs/jsDoc.md
Normal file
130
docs/jsDoc.md
Normal file
@ -0,0 +1,130 @@
|
||||
# [Javascript][JSDoc]ドキュメントを自動生成する(TypeScript対応)
|
||||
|
||||
* JSDocコメントの有効活用
|
||||
* JavaScriptやTypeScriptのドキュメントを生成する方法
|
||||
|
||||
- [\[Javascript\]\[JSDoc\]ドキュメントを自動生成する(TypeScript対応)](#javascriptjsdocドキュメントを自動生成するtypescript対応)
|
||||
- [インストール方法](#インストール方法)
|
||||
- [packeage.jsonの設定](#packeagejsonの設定)
|
||||
- [TypeScriptの場合](#typescriptの場合)
|
||||
- [書き方について](#書き方について)
|
||||
- [ファイルを出力する](#ファイルを出力する)
|
||||
- [1つの対象ファイルを出力する](#1つの対象ファイルを出力する)
|
||||
- [markdownで出力する場合](#markdownで出力する場合)
|
||||
- [HTMLで出力する場合](#htmlで出力する場合)
|
||||
|
||||
|
||||
## インストール方法
|
||||
|
||||
```sh
|
||||
npm install jsdoc -D
|
||||
```
|
||||
|
||||
### packeage.jsonの設定
|
||||
|
||||
スクリプトを記載する
|
||||
`-r`オプションを用いて、出力させたい対象のフォルダーを指定します
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"doc": "jsdoc -r src"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScriptの場合
|
||||
|
||||
インストール方法
|
||||
|
||||
```sh
|
||||
npm install typedoc -D
|
||||
```
|
||||
packeage.jsonの設定
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"doc": "typedoc --entryPointStrategy expand ./src"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
tsconfig.jsonファイルの設定
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"sourceMap": true,
|
||||
"strict": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 書き方について
|
||||
|
||||
JSDocはJavascriptのコメントルールです
|
||||
VSCodeとの相性が良い。プラグインなしで自動で生成できる
|
||||
|
||||
* コメントが必要なところで`/**`を入力すると自動的に生成される
|
||||
* autocompleteが自動に表示しますので、`Enter`を入力する
|
||||
|
||||
```js
|
||||
/**
|
||||
```
|
||||
|
||||
## ファイルを出力する
|
||||
|
||||
### 1つの対象ファイルを出力する
|
||||
|
||||
#### markdownで出力する場合
|
||||
|
||||
**markdownのモジュールをインストール**
|
||||
|
||||
```sh
|
||||
npm install -g jsdoc-to-markdown
|
||||
```
|
||||
|
||||
**ファイルを出力する**
|
||||
|
||||
```sh
|
||||
npx jsdoc-to-markdown your-file.js > output.md
|
||||
# npx jsdoc-to-markdown src/script/sampleScript.js > output.md
|
||||
```
|
||||
|
||||
#### HTMLで出力する場合
|
||||
|
||||
**ファイルを直接指定する場合**
|
||||
|
||||
```sh
|
||||
jsdoc your-file.js -d doc
|
||||
# jsdoc src/script/sampleScript.js -d docments
|
||||
```
|
||||
|
||||
**設定ファイルを仕様する場合**
|
||||
|
||||
|
||||
`jsdoc.json`を生成する
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"include": ["src/script/sampleScript.js"]
|
||||
},
|
||||
"opts": {
|
||||
"destination": "./docments"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**ファイルを出力する**
|
||||
|
||||
```sh
|
||||
jsdoc -c jsdoc.json
|
||||
```
|
||||
|
319
docs/mermaid.md
Normal file
319
docs/mermaid.md
Normal file
@ -0,0 +1,319 @@
|
||||
# 【Mermaid】概要及び基本的な使い方
|
||||
|
||||
- [【Mermaid】概要及び基本的な使い方](#mermaid概要及び基本的な使い方)
|
||||
- [各図形の基本的な書き方](#各図形の基本的な書き方)
|
||||
- [フローチャート図](#フローチャート図)
|
||||
- [シーケンス図](#シーケンス図)
|
||||
- [クラス図](#クラス図)
|
||||
- [ER図(データ概念図 CDMD)](#er図データ概念図-cdmd)
|
||||
- [アーキテクチャ図](#アーキテクチャ図)
|
||||
- [ガントチャート](#ガントチャート)
|
||||
- [図形の設定について](#図形の設定について)
|
||||
- [テーマの設定](#テーマの設定)
|
||||
- [Tips](#tips)
|
||||
- [VSCodeにスぺニットを設定する(シーケンス図)](#vscodeにスぺニットを設定するシーケンス図)
|
||||
|
||||
|
||||
**(参考リンク)**
|
||||
* [公式サイト](https://mermaid.js.org/intro/)
|
||||
* [公式 WEB Editor](https://mermaid.live/edit#p)
|
||||
* [notepm-Mermaid記法の書き方](https://help.notepm.jp/hc/ja/articles/17267311368729-Mermaid%E8%A8%98%E6%B3%95%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9-Markdown%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%81%A7%E3%83%81%E3%83%A3%E3%83%BC%E3%83%88-%E3%82%B0%E3%83%A9%E3%83%95%E3%81%8C%E6%8F%8F%E3%81%91%E3%82%8B-
|
||||
)
|
||||
|
||||
Mermaidが対応していないコンテンツでも公式サイトのエディタを
|
||||
活用することで図形をリンクさせることが可能です
|
||||
|
||||
---
|
||||
|
||||
## 各図形の基本的な書き方
|
||||
|
||||
### フローチャート図
|
||||
|
||||
````
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
```
|
||||
````
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
```
|
||||
|
||||
### シーケンス図
|
||||
|
||||
* **古いバージョンではactor、participantが対応していない**
|
||||
|
||||
````
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor User
|
||||
participant Front
|
||||
participant Server
|
||||
User->>Front: URLをリンクする
|
||||
Front-->>User: 一覧画面を表示する
|
||||
User->>Front: 検索する
|
||||
loop 対象商品
|
||||
Front->>Server: 商品情報を取得する
|
||||
Server-->>Front: レスポンス
|
||||
end
|
||||
Front-->>User: 検索結果を表示する
|
||||
Note right of Front: Product Find <br/>sequence
|
||||
```
|
||||
````
|
||||
|
||||
[](https://mermaid.live/edit#pako:eNptkU9LwzAYxr9KyHnFe5GexJOKOHaRXmKTbYU1qVkiyBi4Fr14UPyLsIkbQ0WEMVRwCvsysev8FqatpYi7Je_7e9734X1a0GGYQBM2ya4k1CErLqpx5NncpgAgRzAOKk3Cs7-PuHAd10dUgFXOqPhfLhO-l-GJzLCslDNBZWtNBWcqfFLhiwpGqnOjguMES_uGBhPeBF_vB_P7h_ji87s30IJ5_zEefhT036GzYTd-HRTdBmM-iEbT-bgfXR5F551igWVlzkyQdWbhYXQ31huik6toel3MyDCjWKLCZxVMVNhLnU8ShlC8wHrmJn47nd12F1jfYIIA7tbqArAq-B2-yRmWjr6mSzFY3uFLVh5EIoEl6BHuIRfrhFrJqW0o6sQjNjT1E5Mqkg1hQ5u2NYqkYOV96kBTcElKUPoYiTxQaFZRo6mrBLs61PUs9TT8EuRM1uq5TMe4zVguaP8AZJXvtw)
|
||||
|
||||
APIのサンプル例
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
App ->> API: request
|
||||
alt OK
|
||||
API -->> App: 200
|
||||
else error
|
||||
API -->> App: 400
|
||||
end
|
||||
```
|
||||
|
||||
### クラス図
|
||||
|
||||
````
|
||||
```mermaid
|
||||
classDiagram
|
||||
Animal <|-- Duck
|
||||
Animal <|-- Zebra
|
||||
Animal : +int age
|
||||
Animal : +String gender
|
||||
Animal: +mate()
|
||||
class Duck{
|
||||
+String beakColor
|
||||
+swim()
|
||||
+quack()
|
||||
}
|
||||
class Zebra{
|
||||
+bool is_wild
|
||||
+run()
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
Animal <|-- Duck
|
||||
Animal <|-- Zebra
|
||||
Animal : +int age
|
||||
Animal : +String gender
|
||||
Animal: +mate()
|
||||
class Duck{
|
||||
+String beakColor
|
||||
+swim()
|
||||
+quack()
|
||||
}
|
||||
class Zebra{
|
||||
+bool is_wild
|
||||
+run()
|
||||
}
|
||||
```
|
||||
|
||||
### ER図(データ概念図 CDMD)
|
||||
|
||||
データベースのスキーマを表現したりするのに使われるER図です
|
||||
「概念データモデル(CDM: Conceptual Data Model)」
|
||||
|
||||
````
|
||||
```mermaid
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ LINE-ITEM : contains
|
||||
CUSTOMER }|..|{ DELIVERY-ADDRESS : uses
|
||||
```
|
||||
````
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ LINE-ITEM : contains
|
||||
CUSTOMER }|..|{ DELIVERY-ADDRESS : uses
|
||||
```
|
||||
|
||||
(リレーション用のsymbol)
|
||||
* `||--||` : 1対1 (One to One)
|
||||
* `}o--||` : 多対1 (Many to One)
|
||||
* `o{--||` : 1対多 (One to Many)
|
||||
* `}o--o{` : 多対多 (Many to Many)
|
||||
|
||||
### アーキテクチャ図
|
||||
|
||||
**古いバージョンではアーキテクチャ図が対応していない**
|
||||
|
||||
````
|
||||
```mermaid
|
||||
architecture-beta
|
||||
group public_api(cloud)[Public API]
|
||||
group private_api(cloud)[Private API] in public_api
|
||||
service database(database)[Database] in private_api
|
||||
service server(server)[Server] in private_api
|
||||
|
||||
server:R -- L:database
|
||||
```
|
||||
````
|
||||
|
||||
[](https://mermaid.live/edit#pako:eNptkMFqwzAQRH9F7MkG-wd0K-RSaCE0t1qlrKWNLbAlsZYCJeTfK9sVTUN12WH1ZhbmCtobAgnIerSRdExMbU8RFSsn8hvYpyBC6ierPzHYSk8-mbo7bhvxdHz-eEDZXjDSH3ZfbbCw7i6tWBfii9UkDEbscaGqiLo7_Kjd-Rv-aF0ncbWPujtt8z_TvZFYvom2FS-yHFx_oYGZeEZrcjPXlVYQR5pJgczS0BnTFBUod8sopuhPX06DjJyogRRyFh0sDoxzWZKx0fPrXvbWeQO5rmEsQED37n3mzzgtdPsG-VmP1Q)
|
||||
|
||||
### ガントチャート
|
||||
|
||||
````
|
||||
```mermaid
|
||||
gantt
|
||||
title work
|
||||
dateFormat YYYY-MM-DD
|
||||
section work_A
|
||||
準備 :a1 ,2023-08-12 ,1d
|
||||
作業 :a2 ,2023-08-14 ,2d
|
||||
リリース :a3 ,after a2 ,1d
|
||||
|
||||
section work_B
|
||||
準備 :b1 ,after a2 ,1d
|
||||
作業_1 :b2 ,after b1 ,3d
|
||||
作業_2 :b3 ,after b1 ,2d
|
||||
リリース :b4 ,after b2 ,1d
|
||||
```
|
||||
````
|
||||
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title work
|
||||
dateFormat YYYY-MM-DD
|
||||
section work_A
|
||||
準備 :a1 ,2023-08-12 ,1d
|
||||
作業 :a2 ,2023-08-14 ,2d
|
||||
リリース :a3 ,after a2 ,1d
|
||||
|
||||
section work_B
|
||||
準備 :b1 ,after a2 ,1d
|
||||
作業_1 :b2 ,after b1 ,3d
|
||||
作業_2 :b3 ,after b1 ,2d
|
||||
リリース :b4 ,after b2 ,1d
|
||||
```
|
||||
|
||||
## 図形の設定について
|
||||
|
||||
### テーマの設定
|
||||
|
||||
**参考記事:**
|
||||
* [zenn-Mermaid のテーマ・スタイルの変更方法](https://zenn.dev/junkawa/articles/zenn-mermaidjs-theme-config)
|
||||
|
||||
|
||||
|
||||
```sh
|
||||
%%{init:{'theme':'base'}}%%
|
||||
```
|
||||
|
||||
**default**
|
||||
|
||||
設定しない場合もこのスタイルになります
|
||||
|
||||
```mermaid
|
||||
%%{init:{'theme':'default'}}%%
|
||||
graph LR
|
||||
q(QEMU) --> qemu-boot-shim --> physboot --> zircon
|
||||
```
|
||||
|
||||
**base**
|
||||
|
||||
```mermaid
|
||||
%%{init:{'theme':'base'}}%%
|
||||
graph LR
|
||||
q(QEMU) --> qemu-boot-shim --> physboot --> zircon
|
||||
```
|
||||
|
||||
**forest**
|
||||
|
||||
```mermaid
|
||||
%%{init:{'theme':'forest'}}%%
|
||||
graph LR
|
||||
q(QEMU) --> qemu-boot-shim --> physboot --> zircon
|
||||
```
|
||||
|
||||
**dark**
|
||||
|
||||
```mermaid
|
||||
%%{init:{'theme':'dark'}}%%
|
||||
graph LR
|
||||
q(QEMU) --> qemu-boot-shim --> physboot --> zircon
|
||||
```
|
||||
|
||||
**neutral**
|
||||
|
||||
```mermaid
|
||||
%%{init:{'theme':'neutral'}}%%
|
||||
graph LR
|
||||
q(QEMU) --> qemu-boot-shim --> physboot --> zircon
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
### VSCodeにスぺニットを設定する(シーケンス図)
|
||||
|
||||
1. [Ctrl] + [Shift] + [P]を入力する
|
||||
2. "Snippets: Configure Snippets"を入力する
|
||||
3. mermaid.jsonというスニペットファイルを作成する
|
||||
4. 使って確かめてみる
|
||||
1. Mermaidをサポートするファイルを開く(.mdまたは.mmd)
|
||||
2. "mermaid:sequence"と入力する
|
||||
3. Tabキーを押下する
|
||||
|
||||
```json
|
||||
{
|
||||
"Mermaid sequence Diagram": {
|
||||
"prefix": "mermaid:sequence",
|
||||
|
||||
"body": [
|
||||
"sequenceDiagram",
|
||||
" actor User",
|
||||
" participant Front",
|
||||
" participant Server",
|
||||
" User->>Front: Click Button",
|
||||
" Front->>Server: Request Data",
|
||||
" Server-->>Front: Return Data",
|
||||
" Front-->>User: Display Data"
|
||||
],
|
||||
"description": "Create a sequence diagram"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
でない場合はデフォルトでスニペットが無効に
|
||||
なっている場合があります
|
||||
|
||||
markdownでも有効にする場合は`markdown.json`に以下を追記する
|
||||
|
||||
```json
|
||||
"Mermaid sequence Diagram": {
|
||||
"prefix": "mermaid:sequence",
|
||||
"body": [
|
||||
"```mermaid",
|
||||
"sequenceDiagram",
|
||||
" actor User",
|
||||
" participant Front",
|
||||
" participant Server",
|
||||
" User->>Front: Click Button",
|
||||
" Front->>Server: Request Data",
|
||||
" Server-->>Front: Return Data",
|
||||
" Front-->>User: Display Data",
|
||||
"```"
|
||||
],
|
||||
"description": "Create a sequence diagram"
|
||||
}
|
||||
```
|
95
docs/vs-code-md.md
Normal file
95
docs/vs-code-md.md
Normal file
@ -0,0 +1,95 @@
|
||||
|
||||
# [VSCODE]MarkDownを活用する
|
||||
|
||||
- [\[VSCODE\]MarkDownを活用する](#vscodemarkdownを活用する)
|
||||
- [ライブラリ・機能一覧](#ライブラリ機能一覧)
|
||||
- [基本的な操作](#基本的な操作)
|
||||
- [Markdown All in One](#markdown-all-in-one)
|
||||
- [Markdown PDF](#markdown-pdf)
|
||||
- [Markdown Preview Enhanced](#markdown-preview-enhanced)
|
||||
- [Markdown Preview Mermaid Support](#markdown-preview-mermaid-support)
|
||||
- [Markdown Table](#markdown-table)
|
||||
- [Excel to Markdown table](#excel-to-markdown-table)
|
||||
- [Tips](#tips)
|
||||
- [目次作成機能](#目次作成機能)
|
||||
|
||||
**(参考リンク)**
|
||||
* [github markdown-preview-enhanced](https://github.com/shd101wyy/markdown-preview-enhanced/blob/master/docs/ja-jp/file-imports.md)
|
||||
* [qiita 便利な拡張機能5選](https://qiita.com/sola-msr/items/cfe448db958da3d08863#excel-to-markdown-table)
|
||||
|
||||
|
||||
|
||||
## ライブラリ・機能一覧
|
||||
|
||||
* Markdown All in One
|
||||
* Markdown PDF
|
||||
* Markdown Preview Enhanced :PDF出力機能
|
||||
|
||||
### 基本的な操作
|
||||
|
||||
#### Markdown All in One
|
||||
|
||||
1. キーボードショートカット機能
|
||||
1. コード入力候補/補助
|
||||
2. 目次作成機能(ctrl+shift+p -> Markdown All in One: Create Table of Contents)
|
||||
3. 自動補完機能
|
||||
1. 見出しナンバリング
|
||||
2. リスト編集補完
|
||||
3. HTML変換機能
|
||||
4. その他機能
|
||||
1. 数式機能
|
||||
2. テーブル等幅機能
|
||||
5. 画像リンク貼付機能
|
||||
|
||||
コード入力候補/補助
|
||||
|
||||
#### Markdown PDF
|
||||
|
||||
1. PDF出力機能
|
||||
|
||||
#### Markdown Preview Enhanced
|
||||
|
||||
(サイド:Ctrl+K -> V,全面:ctrl+shift+v)
|
||||
|
||||
1. 拡張性の高いマークダウンのプレビュー拡張機能
|
||||
2. ファイル分割機能
|
||||
|
||||
##### Markdown Preview Mermaid Support
|
||||
|
||||
MermaidをViewに表示できるようにする
|
||||
|
||||
[【Mermaid】概要及び基本的な使い方](https://wiki.pglikers.com/en/private/docs/markdown/mermaid)
|
||||
|
||||
#### Markdown Table
|
||||
|
||||
1. 等幅機能、入力補完機能:テーブルを作成中に[`Tab`]キーを使用する
|
||||
|
||||
#### Excel to Markdown table
|
||||
|
||||
エクセル、スプレッドシートからテーブルをコピーする
|
||||
(Shift + Alt + v )
|
||||
|
||||
- Markdown Preview Mermaid Support : MermaidをViewに表示できるようにする
|
||||
|
||||
---
|
||||
|
||||
## Tips
|
||||
|
||||
### キーボードショートカット機能
|
||||
|
||||
| キー | 説明 |
|
||||
| ---------------- | ---------------------------- |
|
||||
| **Ctrl+B** | 選択した文字の太字にする。 |
|
||||
| **Ctrl+I** | 文字を斜体 |
|
||||
| **Ctrl+K -> V** | エディタの横にプレビュー表示 |
|
||||
| **Ctrl+Shift+]** | 見出しレベルを上げる |
|
||||
| **Ctrl+Shift+[** | 見出しレベルを下げる |
|
||||
| Ctrl+M | 数式入力の文字入れる($$) |
|
||||
| Alt+C | チェックリストのオンオフ |
|
||||
| Ctrl+Shift+V | プレビューの表示切替 |
|
||||
|
||||
### 目次作成機能
|
||||
|
||||
1. [`ctrl` +`shift` + `p`]
|
||||
2. "Markdown All in One: Create Table of Contents"を選択する
|
||||
|
0
keys/.gitkeep
Normal file
0
keys/.gitkeep
Normal file
2120
package-lock.json
generated
Normal file
2120
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@ -2,13 +2,23 @@
|
||||
"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": "",
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@google-cloud/storage": "^7.15.2",
|
||||
"archiver": "^7.0.1",
|
||||
"csv-writer": "^1.6.0",
|
||||
"dotenv": "^16.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jsdoc": "^4.0.4"
|
||||
}
|
||||
}
|
||||
|
70
src/classes/TimeWatch.js
Normal file
70
src/classes/TimeWatch.js
Normal file
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* TimeWatch class
|
||||
* @class
|
||||
* @classdesc TimeWatch class to measure elapsed time
|
||||
*/
|
||||
class TimeWatch{
|
||||
/**
|
||||
* TimeWatch constructor
|
||||
* @constructor
|
||||
* @description TimeWatch constructor
|
||||
* @param {number} startTime - start time
|
||||
* @param {number} endTime - end time
|
||||
*/
|
||||
constructor(){
|
||||
this.startTime = 0;
|
||||
this.endTime = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the timer
|
||||
* @function
|
||||
* @description Start the timer
|
||||
*/
|
||||
start(){
|
||||
this.startTime = new Date().getTime();
|
||||
}
|
||||
/**
|
||||
* Stop the timer
|
||||
* @function
|
||||
* @description Stop the timer
|
||||
*/
|
||||
stop(){
|
||||
this.endTime = new Date().getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the elapsed time
|
||||
* @function
|
||||
* @description Get the elapsed time
|
||||
* @param {string} unit - unit of time (ms, s, min, h)
|
||||
* @returns {number} - elapsed time
|
||||
*/
|
||||
getElapsedTime(unit = "ms") {
|
||||
const elapsedTime = this.endTime - this.startTime;
|
||||
|
||||
switch (unit) {
|
||||
case "s":
|
||||
return elapsedTime / 1000;
|
||||
case "min":
|
||||
return elapsedTime / (1000 * 60);
|
||||
case "h":
|
||||
return elapsedTime / (1000 * 60 * 60);
|
||||
case "ms":
|
||||
default:
|
||||
return elapsedTime;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Print the elapsed time
|
||||
* @function
|
||||
* @description Print the elapsed time
|
||||
* @param {string} unit - unit of time (ms, s, min, h)
|
||||
*/
|
||||
printElapsedTime(unit = "ms") {
|
||||
console.log(`Elapsed Time: ${this.getElapsedTime(unit)} ${unit}`);
|
||||
}
|
||||
}
|
||||
|
||||
// commonJS module
|
||||
module.exports = TimeWatch;
|
0
src/front/assets/cloud-storage.js
Normal file
0
src/front/assets/cloud-storage.js
Normal file
BIN
src/front/assets/data/test.zip
Normal file
BIN
src/front/assets/data/test.zip
Normal file
Binary file not shown.
11
src/front/assets/data/test_data_1.csv
Normal file
11
src/front/assets/data/test_data_1.csv
Normal file
@ -0,0 +1,11 @@
|
||||
ID,Name,Age,Email
|
||||
1,User_1,44,user1@example.com
|
||||
2,User_2,20,user2@example.com
|
||||
3,User_3,67,user3@example.com
|
||||
4,User_4,49,user4@example.com
|
||||
5,User_5,56,user5@example.com
|
||||
6,User_6,63,user6@example.com
|
||||
7,User_7,31,user7@example.com
|
||||
8,User_8,67,user8@example.com
|
||||
9,User_9,41,user9@example.com
|
||||
10,User_10,30,user10@example.com
|
|
11
src/front/assets/data/test_data_2.csv
Normal file
11
src/front/assets/data/test_data_2.csv
Normal file
@ -0,0 +1,11 @@
|
||||
ID,Name,Age,Email
|
||||
1,User_1,37,user1@example.com
|
||||
2,User_2,30,user2@example.com
|
||||
3,User_3,63,user3@example.com
|
||||
4,User_4,42,user4@example.com
|
||||
5,User_5,31,user5@example.com
|
||||
6,User_6,63,user6@example.com
|
||||
7,User_7,28,user7@example.com
|
||||
8,User_8,20,user8@example.com
|
||||
9,User_9,42,user9@example.com
|
||||
10,User_10,69,user10@example.com
|
|
79
src/front/assets/download.js
Normal file
79
src/front/assets/download.js
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @description downloadボタンを押下したらCSVファイルをzip圧縮してダウンロードする
|
||||
*/
|
||||
|
||||
|
||||
// ダウンロードボタン
|
||||
const downloadButton = document.getElementById('download-button');
|
||||
const downloadButtonFile = document.getElementById('download-button-file');
|
||||
|
||||
// ダウンロードボタンを押下した時の処理を記載する
|
||||
downloadButton.addEventListener('click', async () => {
|
||||
// ダウンロードボタンを無効化する
|
||||
downloadButton.disabled = true;
|
||||
// ダウンロード処理を実行する
|
||||
try {
|
||||
const zip = new JSZip();
|
||||
// ZIPにファイルを追加
|
||||
zip.file("hello.txt", "Hello, this is a ZIP file!");
|
||||
zip.file("world.txt", "Hello, this is a ZIP file!");
|
||||
|
||||
// ZIPを生成してダウンロード
|
||||
const zipBlob = await zip.generateAsync({ type: "blob" });
|
||||
const a = document.createElement("a");
|
||||
a.href = URL.createObjectURL(zipBlob);
|
||||
a.download = "example.zip";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
} catch (e) {
|
||||
// エラーが発生した場合
|
||||
console.error(e);
|
||||
alert('ダウンロードに失敗しました');
|
||||
} finally {
|
||||
// ダウンロードボタンを有効化する
|
||||
downloadButton.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
downloadButtonFile.addEventListener('click', async () => {
|
||||
// ダウンロードボタンを無効化する
|
||||
downloadButtonFile.disabled = true;
|
||||
try {
|
||||
const files = [
|
||||
{ name: "text1.csv", url: "assets/data/test_data_1.csv" },
|
||||
{ name: "text2.csv", url: "assets/data/test_data_2.csv" }
|
||||
];
|
||||
|
||||
const zip = new JSZip();
|
||||
|
||||
// CSV ファイルを取得して ZIP に追加
|
||||
await Promise.all(
|
||||
files.map(async (file) => {
|
||||
const response = await fetch(file.url);
|
||||
if (!response.ok) throw new Error(`Failed to fetch ${file.name}`);
|
||||
const text = await response.text();
|
||||
zip.file(file.name, text);
|
||||
})
|
||||
);
|
||||
|
||||
// ZIP を生成してダウンロード
|
||||
zip.generateAsync({ type: "blob" }).then((blob) => {
|
||||
const link = document.createElement("a");
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = "files.zip";
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}).catch(console.error)
|
||||
} catch (e) {
|
||||
// エラーが発生した場合
|
||||
console.error(e);
|
||||
alert('ダウンロードに失敗しました');
|
||||
}
|
||||
finally {
|
||||
// ダウンロードボタンを有効化する
|
||||
downloadButtonFile.disabled = false;
|
||||
}
|
||||
});
|
72
src/front/assets/downloadFromServer.js
Normal file
72
src/front/assets/downloadFromServer.js
Normal file
@ -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;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
13
src/front/assets/lib/jszip.min.js
vendored
Normal file
13
src/front/assets/lib/jszip.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
21
src/front/download.html
Normal file
21
src/front/download.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!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>Download</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>JavaScript Develop Download To Front Side</h1>
|
||||
<p >Zip File Download</p>
|
||||
<button id="download-button">Zip Download(From Content)</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/downloadFromServer.js"></script>
|
||||
</body>
|
||||
</html>
|
15
src/front/gcs.html
Normal file
15
src/front/gcs.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!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>
|
@ -8,6 +8,6 @@
|
||||
<body>
|
||||
<h1>JavaScript Develop Debug</h1>
|
||||
<p id="message">Open the console to see the output</p>
|
||||
<script src="index.js"></script>
|
||||
<script src="assets/index.js"></script>
|
||||
</body>
|
||||
</html>
|
129
src/script/archiveFiles.js
Normal file
129
src/script/archiveFiles.js
Normal file
@ -0,0 +1,129 @@
|
||||
|
||||
/**
|
||||
* @requires archiver
|
||||
* - npm install archiver
|
||||
* @example
|
||||
* node src/script/archiveFiles.js data output.zip
|
||||
*/
|
||||
const archiver = require('archiver');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const TimeWatch = require('../classes/TimeWatch');
|
||||
|
||||
|
||||
/**
|
||||
* 特定のディレクトリからzipファイルを作成する
|
||||
*
|
||||
* @param {*} sourceDir
|
||||
* @param {*} outputFilePath
|
||||
* @returns
|
||||
*/
|
||||
const createZipFile = (sourceDir, outputFilePath) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const output = fs.createWriteStream(outputFilePath);
|
||||
const archive = archiver('zip', {
|
||||
zlib: { level: 9 } // 圧縮レベル
|
||||
});
|
||||
output.on('close', () => {
|
||||
console.log(`ZIPファイル作成完了: ${outputFilePath}`);
|
||||
resolve();
|
||||
});
|
||||
archive.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
archive.pipe(output);
|
||||
archive.directory(sourceDir, false);
|
||||
archive.finalize();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* ディレクトリのサイズを測定する
|
||||
* @description デバッグ用途で本番ではコメントアウトする
|
||||
* @param {*} dir
|
||||
* @returns
|
||||
*/
|
||||
const _debugDirectorySize = (dir) => {
|
||||
let size = 0;
|
||||
const files = fs.readdirSync(dir);
|
||||
for (let file of files) {
|
||||
const filePath = path.join(dir, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
if (stat.isFile()) {
|
||||
size += stat.size;
|
||||
} else if (stat.isDirectory()) {
|
||||
size += _getDirectorySize(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
// デバッグ用にサイズを表示する
|
||||
if (size < 1000) {
|
||||
// 1000バイトまでの場合はbytesで表示する
|
||||
console.log(`Directory: ${dir}, Size: ${size} Biytes`);
|
||||
} else if(size < 1000000) {
|
||||
// 1000バイト以上、1000000バイト未満の場合はKBで表示する 小数点は第2位まで表示する
|
||||
console.log(`Directory: ${dir}, Size: ${Math.round(size / 1000 * 100) / 100} KB`);
|
||||
} else if(size < 1000000000) {
|
||||
// 1000000バイト以上、1000000000バイト未満の場合はMBで表示する 小数点は第2位まで表示する
|
||||
console.log(`Directory: ${dir}, Size: ${Math.round(size / 1000000 * 100) / 100} MB`);
|
||||
} else {
|
||||
// 1000000000バイト以上の場合はGBで表示する 小数点は第2位まで表示する
|
||||
console.log(`Directory: ${dir}, Size: ${Math.round(size / 1000000000 * 100) / 100} GB`);
|
||||
}
|
||||
return size;
|
||||
};
|
||||
|
||||
/**
|
||||
* エントリーポイント
|
||||
*
|
||||
* @function main
|
||||
* @param {string[]} args - コマンドライン引数 (process.argv を除いたもの)
|
||||
* @returns {void}
|
||||
*
|
||||
* @example
|
||||
* // フォルダ data を output.zip に圧縮する
|
||||
* node src/script/archiveFiles.js csv_output output.zip
|
||||
*
|
||||
*/
|
||||
const main = (args) => {
|
||||
if (args.includes('--help')) {
|
||||
console.log(`
|
||||
Usage: node archiveFiles.js [sourceDir] [outputFile]
|
||||
|
||||
args:
|
||||
sourceDir : 圧縮するディレクトリ
|
||||
outputFile : 出力ファイル名
|
||||
|
||||
Options:
|
||||
--help ヘルプを表示
|
||||
`);
|
||||
return
|
||||
}
|
||||
|
||||
// 引数が2つ以外の場合はエラー
|
||||
if (args.length !== 2) {
|
||||
console.error('エラー: 引数が正しくありません。');
|
||||
console.error('Usage: node script.js [sourceDir] [outputFile]');
|
||||
return;
|
||||
}
|
||||
const sourceDir = args[0];
|
||||
// ファイルの存在チェック
|
||||
if (!fs.existsSync(sourceDir)) {
|
||||
console.error(`エラー: ディレクトリが存在しません: ${sourceDir}`);
|
||||
return;
|
||||
}
|
||||
const outputFilePath = args[1];
|
||||
// prodモード以外の場合は表示する
|
||||
if(process.env.NODE_ENV !== 'production') _debugDirectorySize(sourceDir);
|
||||
|
||||
const timeWatch = new TimeWatch();
|
||||
timeWatch.start();
|
||||
createZipFile(sourceDir, outputFilePath);
|
||||
timeWatch.stop();
|
||||
timeWatch.printElapsedTime();
|
||||
}
|
||||
|
||||
|
||||
console.log(`start ${process.argv[1]}`);
|
||||
main(process.argv.slice(2));
|
26
src/script/gcs.js
Normal file
26
src/script/gcs.js
Normal file
@ -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();
|
||||
|
73
src/script/generateCSV.js
Normal file
73
src/script/generateCSV.js
Normal file
@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* CSVファイルを生成するスクリプト
|
||||
*
|
||||
* @requires csv-writer
|
||||
* - npm install csv-writer
|
||||
* @example
|
||||
* // コマンドラインから実行する場合:
|
||||
* node src/script/generateCSV.js
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const createCsvWriter = require('csv-writer').createObjectCsvWriter;
|
||||
|
||||
|
||||
/**
|
||||
* 乱数を使ってダミーデータを生成する
|
||||
* @param {*} numRecords
|
||||
* @returns
|
||||
*/
|
||||
const generateData = (numRecords) => {
|
||||
const data = [];
|
||||
for (let i = 1; i <= numRecords; i++) {
|
||||
data.push({
|
||||
id: i,
|
||||
name: `User_${i}`,
|
||||
age: Math.floor(Math.random() * 50) + 20, // 20~69歳
|
||||
email: `user${i}@example.com`,
|
||||
});
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 指定した数のCSVファイルを作成
|
||||
* @param {*} numFiles
|
||||
* @param {*} numRecordsPerFile
|
||||
* @param {*} outputDir
|
||||
*/
|
||||
const createCsvFiles = async (numFiles, numRecordsPerFile, outputDir) => {
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
for (let i = 1; i <= numFiles; i++) {
|
||||
const filePath = path.join(outputDir, `test_data_${i}.csv`);
|
||||
|
||||
const csvWriter = createCsvWriter({
|
||||
path: filePath,
|
||||
header: [
|
||||
{ id: 'id', title: 'ID' },
|
||||
{ id: 'name', title: 'Name' },
|
||||
{ id: 'age', title: 'Age' },
|
||||
{ id: 'email', title: 'Email' },
|
||||
],
|
||||
});
|
||||
const data = generateData(numRecordsPerFile);
|
||||
await csvWriter.writeRecords(data);
|
||||
console.log(`CSVファイル作成完了: ${filePath}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// **設定**
|
||||
const numFiles = 3; // 作成するファイル数
|
||||
const numRecordsPerFile = 10; // 各ファイルのレコード数
|
||||
const outputDir = './csv_output'; // 保存ディレクトリ
|
||||
|
||||
createCsvFiles(numFiles, numRecordsPerFile, outputDir)
|
||||
.then(() => console.log('全てのCSVファイルの作成が完了しました!'))
|
||||
.catch(err => console.error('エラー:', err));
|
89
src/script/sampleScript.js
Normal file
89
src/script/sampleScript.js
Normal file
@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* シンプルなコマンドラインスクリプト
|
||||
*
|
||||
* このスクリプトは、引数を受け取り、その内容を表示します。
|
||||
* `--help` オプションを指定すると、使い方を表示します。
|
||||
*
|
||||
* @module script
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* スクリプトのエントリーポイント
|
||||
*
|
||||
* @function main
|
||||
* @param {string[]} args - コマンドライン引数 (process.argv を除いたもの)
|
||||
* @returns {void}
|
||||
*
|
||||
* @example
|
||||
* // メッセージを表示する
|
||||
* node src/script/sampleScript.js Hello
|
||||
*
|
||||
* @example
|
||||
* // ヘルプを表示する
|
||||
* node src/script/sampleScript.js --help
|
||||
*/
|
||||
const main = (args) => {
|
||||
if (args.includes('--help')) {
|
||||
displayHelp();
|
||||
return;
|
||||
}
|
||||
// 引数がない場合のデフォルトメッセージ
|
||||
if (args.length === 0) {
|
||||
handleError('エラー: 引数が必要です。\n`node script.js --help` で使い方を確認してください。');
|
||||
return;
|
||||
}
|
||||
processArguments(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* ヘルプメッセージを表示する
|
||||
*
|
||||
* @function displayHelp
|
||||
* @returns {void}
|
||||
*/
|
||||
const displayHelp = () => {
|
||||
console.log(`
|
||||
Usage: node script.js [message]
|
||||
|
||||
Options:
|
||||
--help ヘルプを表示
|
||||
`);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* エラーメッセージを表示し、終了する
|
||||
*
|
||||
* @function handleError
|
||||
* @param {string} message - エラーメッセージ
|
||||
* @returns {void}
|
||||
*
|
||||
* @example
|
||||
* handleError("エラー: 引数が必要です。");
|
||||
*/
|
||||
const handleError = (message) => {
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 引数を処理してメッセージを表示する
|
||||
*
|
||||
* @function processArguments
|
||||
* @param {string[]} args - コマンドライン引数
|
||||
* @returns {void}
|
||||
*
|
||||
* @example
|
||||
* processArguments(["Hello", "World"]);
|
||||
* // 出力: "Hello World"
|
||||
*/
|
||||
function processArguments(args) {
|
||||
const message = args.join(' ');
|
||||
console.log(`入力されたメッセージ: ${message}`);
|
||||
}
|
||||
|
||||
// コマンドライン引数を取得して main 関数を実行
|
||||
main(process.argv.slice(2));
|
32
src/script/timeWatchTest.js
Normal file
32
src/script/timeWatchTest.js
Normal file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* 処理を測定するスクリプト
|
||||
*
|
||||
* @module script
|
||||
*/
|
||||
|
||||
/**
|
||||
* TimeWatch クラス
|
||||
*/
|
||||
const TimeWatch = require('../classes/TimeWatch');
|
||||
|
||||
/**
|
||||
* スクリプトのエントリーポイント
|
||||
*
|
||||
* @function main
|
||||
* @returns {void}
|
||||
*
|
||||
* node src/script/timeWatchTest.js
|
||||
*/
|
||||
const main = () => {
|
||||
const timeWatch = new TimeWatch();
|
||||
timeWatch.start();
|
||||
// 処理
|
||||
for (let i = 0; i < 100000000; i++) {
|
||||
// do nothing
|
||||
}
|
||||
timeWatch.stop();
|
||||
timeWatch.printElapsedTime();
|
||||
}
|
||||
main();
|
93
src/server/download/download.js
Normal file
93
src/server/download/download.js
Normal file
@ -0,0 +1,93 @@
|
||||
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
|
||||
};
|
46
src/server/download/gcsArchive.js
Normal file
46
src/server/download/gcsArchive.js
Normal file
@ -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;
|
39
src/server/index.js
Normal file
39
src/server/index.js
Normal file
@ -0,0 +1,39 @@
|
||||
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) => {
|
||||
// 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ファイルのコンテンツをそのまま返す
|
||||
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;
|
||||
server.listen(port, () => {
|
||||
console.log(`Server running at http://localhost:${port}/`);
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user