Compare commits

..

No commits in common. "fa31073482958d9f944878e5fe8e75b0ed42fb04" and "183eb20fd99cb3943567f05c082366c5a74e5e9d" have entirely different histories.

48 changed files with 4 additions and 5306 deletions

5
.gitignore vendored
View File

@ -130,8 +130,3 @@ dist
.yarn/install-state.gz .yarn/install-state.gz
.pnp.* .pnp.*
csv_output/
output/
output.*
docments/
keys/

View File

@ -1,7 +1,7 @@
# js-common-code # js-common-code
Javascriptで活用できるコードを開発する。 Javascriptで活用できるコードを開発する。
Node.jsによりサーバーサイドも作成 Node.jsによりサーバーサイドも作成する

View File

@ -1,217 +0,0 @@
# [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;
```

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,56 +0,0 @@
# [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
```

View File

@ -1,86 +0,0 @@
# [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}`);
});
```

View File

@ -1,130 +0,0 @@
# [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
```

View File

@ -1,319 +0,0 @@
# 【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
```
````
[![sequence](https://mermaid.ink/img/pako:eNptkU9LwzAYxr9KyHnFe5GexJOKOHaRXmKTbYU1qVkiyBi4Fr14UPyLsIkbQ0WEMVRwCvsysev8FqatpYi7Je_7e9734X1a0GGYQBM2ya4k1CErLqpx5NncpgAgRzAOKk3Cs7-PuHAd10dUgFXOqPhfLhO-l-GJzLCslDNBZWtNBWcqfFLhiwpGqnOjguMES_uGBhPeBF_vB_P7h_ji87s30IJ5_zEefhT036GzYTd-HRTdBmM-iEbT-bgfXR5F551igWVlzkyQdWbhYXQ31huik6toel3MyDCjWKLCZxVMVNhLnU8ShlC8wHrmJn47nd12F1jfYIIA7tbqArAq-B2-yRmWjr6mSzFY3uFLVh5EIoEl6BHuIRfrhFrJqW0o6sQjNjT1E5Mqkg1hQ5u2NYqkYOV96kBTcElKUPoYiTxQaFZRo6mrBLs61PUs9TT8EuRM1uq5TMe4zVguaP8AZJXvtw?type=png)](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.ink/img/pako:eNptkMFqwzAQRH9F7MkG-wd0K-RSaCE0t1qlrKWNLbAlsZYCJeTfK9sVTUN12WH1ZhbmCtobAgnIerSRdExMbU8RFSsn8hvYpyBC6ierPzHYSk8-mbo7bhvxdHz-eEDZXjDSH3ZfbbCw7i6tWBfii9UkDEbscaGqiLo7_Kjd-Rv-aF0ncbWPujtt8z_TvZFYvom2FS-yHFx_oYGZeEZrcjPXlVYQR5pJgczS0BnTFBUod8sopuhPX06DjJyogRRyFh0sDoxzWZKx0fPrXvbWeQO5rmEsQED37n3mzzgtdPsG-VmP1Q?type=png)](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"
}
```

View File

@ -1,199 +0,0 @@
# MinIO 利用ガイド
## 概要
[MinIO](https://min.io/) は Amazon S3 互換のオブジェクトストレージサーバー。軽量かつシンプルな構成で、オンプレミスやクラウド上に S3 互換のストレージ環境を構築できる。
高可用構成やセキュリティ機能も備えており、小規模から大規模用途まで幅広く利用できる。本記事では MinIO の導入方法、仕様要件、ユースケース、基本的な利用方法についてまとめる。
---
## 1. インストール方法
### 1.1 Docker を使う場合
```bash
docker run -p 9000:9000 -p 9001:9001 \
--name minio \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=password123" \
-v /mnt/data:/data \
quay.io/minio/minio server /data --console-address ":9001"
```
- `9000`: S3互換API用ポート
- `9001`: Webコンソール用ポート
![mino-top.png](/mino-top.png)
---
### 1.2 Docker Compose を使う場合
```yaml
version: '3.8'
services:
minio:
image: quay.io/minio/minio
ports:
- "9000:9000"
- "9001:9001"
volumes:
- ./data:/data
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: password123
command: server /data --console-address ":9001"
restart: unless-stopped
```
起動コマンド:
```bash
docker compose up -d
```
---
### 1.3 バイナリから直接実行する場合Linux
```bash
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
sudo mv minio /usr/local/bin/
export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=password123
minio server /mnt/data --console-address ":9001"
```
---
## 2. 仕様要件
| 項目 | 推奨構成例 |
|--------------|------------------------------|
| OS | LinuxUbuntu 22.04 など) |
| CPU | 1コア以上最低限 |
| メモリ | 1GB 以上2GB 以上推奨) |
| ストレージ | SSD または HDD |
| 通信 | ポート 9000 / 9001開放 |
- HTTPS を使用する場合は TLS 証明書Let's Encrypt など)を導入
- リバースプロキシNginx/Caddyなど経由での公開も可能
---
## 3. ユースケース・機能
### 主なユースケース
- バックアップ保存
- Webアプリの画像・動画ストレージ
- ログデータ保管
- オンプレS3互換ストレージ
### 主な機能
- S3 互換API対応
- Webベース管理コンソール
- IAM風のユーザー・ポリシー制御
- TLS対応HTTPS通信
- サーバーサイド暗号化AES-256
- マルチテナント対応(認証連携含む)
- オブジェクトのバージョン管理(設定時)
---
## 4. 実際の使い方
サンプルコードでは以下を作成しております
1. バケット作成 : sample-data(readwrite)
2. ユーザー設定 : appuser:password123
### Webコンソールを利用する場合
#### Webコンソールにアクセス
ブラウザで `http://<ホスト>:9001` にアクセス
管理者アカウントでログイン(`MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD`
---
#### バケット作成・ファイルアップロード
- Web UI からバケット作成
- ローカルからファイルをアップロード可能
- バケット単位で「パブリックアクセス許可」も可能
### `mc` コマンドによる操作
#### インストール
```bash
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/
```
#### MinIOへの接続
```bash
mc alias set local http://localhost:9000 admin password123
```
#### 操作例
```bash
mc mb local/mybucket
mc cp ./example.jpg local/mybucket/
mc ls local/mybucket
```
### 各プログラム言語のライブラリから利用する場合
MinIOはAmazon S3互換のAPIを提供しているためS3対応の
クライアントライブラリを使用すればPythonやNode.jsから
簡単に接続・操作できます
* Python: `boto3`
* Node.js: `@aws-sdk/client-s3`
**接続時の注意点**
* リージョン
* 仮に指定しますが実際は無視される
* バージョニング/ライフサイクル/通知
* 一部のS3機能はMinIOでサポートされていない、または挙動が違う場合あり
* 署名付きURL
* boto3などで生成する署名付きURLも動作しますが、MinIOの設定によって挙動が異なることがある
### minioライブラリ(MinIO公式)
MinIO専用に最適化されていて、軽量・シンプルで扱いやすいのが特徴です。
S3互換APIを使ってはいますがAmazon純正のSDK@aws-sdk/*と違いMinIOのユースケースに特化してます
#### インストール方法
```sh
npm install minio
```
#### インストール方法
---
## 付録Tips
- リバースプロキシNginxを通して HTTPS 公開することで、ブラウザ互換性やセキュリティを向上できる
- Cloudflare などのCDNと組み合わせれば、簡易的なグローバル配信も可能
- アプリからは AWS SDK を使ってアクセス可能S3互換
---
## まとめ
MinIO は S3互換かつ軽量で、開発・検証・軽量サービスにおけるストレージニーズに柔軟に対応できる。Docker環境でも素早く構築可能なため、内部ツールやバックアップ用途として導入検討する価値がある。

View File

@ -1,95 +0,0 @@
# [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"を選択する

View File

@ -1,25 +0,0 @@
async function main() {
const MinioStorage = require('../src/classes/MinioStorage');
const config = {
endPoint: 'localhost',
port: 9000,
useSSL: false,
accessKey: 'appuser',
secretKey: 'password123',
bucketName: 'sample-data'
}
const storage = new MinioStorage(config);
// ファイルを取得する
files = await storage.getFileList();
console.log(files);
// ファイルをアップロードする
await storage.uploadFile("./src/data/sample2.csv","sample2.csv");
files = await storage.getFileList();
console.log(files);
}
main();

View File

2578
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,24 +2,13 @@
"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": {
"@google-cloud/storage": "^7.15.2",
"archiver": "^7.0.1",
"csv-writer": "^1.6.0",
"dotenv": "^16.4.7",
"minio": "^8.0.5"
},
"devDependencies": {
"jsdoc": "^4.0.4"
}
} }

View File

@ -1,115 +0,0 @@
// @ts-check を付けると型チェックが有効に!
/** @ts-check */
// 型定義をインポート
const Minio = require('minio');
// enum を使っている場合
const { ProcessType } = require('../types/IStorage');
const fs = require('fs');
class MinioStorage {
constructor(config) {
this.minioClient = new Minio.Client({
endPoint: config.endPoint,
port: config.port,
useSSL: config.useSSL,
accessKey: config.accessKey,
secretKey: config.secretKey
});
this.bucketName = config.bucketName;
}
getFileList(prefix = '') {
return new Promise((resolve, reject) => {
const objectKeys = [];
const stream = this.minioClient.listObjects(this.bucketName, prefix, true);
stream.on('data', obj => objectKeys.push(obj.name));
stream.on('end', () => resolve(objectKeys));
stream.on('error', err => reject(err));
});
}
async uploadFile(filePath, objectKey) {
return this.minioClient.fPutObject(this.bucketName, objectKey, filePath)
.then(() => {
console.log('File uploaded successfully.');
})
.catch(err => {
console.error('File upload error:', err);
throw err;
});
}
/**
* オブジェクトをファイルとして保存ローカル
* @param {string} objectKey 取得するオブジェクトキー
* @param {string} destinationPath 保存先ファイルパス
* @returns {Promise<void>}
*/
async downloadFile(objectKey, destinationPath) {
const dataStream = await this.minioClient.getObject(this.bucketName, objectKey);
return new Promise((resolve, reject) => {
const fileStream = fs.createWriteStream(destinationPath);
dataStream.pipe(fileStream);
dataStream.on('end', () => {
console.log('File Download File end');
resolve();
});
dataStream.on('error', reject);
});
}
/**
* オブジェクトをメモリ上に読み込む(Buffer)
* @param {string} objectKey 取得するオブジェクトキー
* @returns {Promise<Buffer>}
*/
async downloadContents(objectKey) {
const dataStream = await this.minioClient.getObject(this.bucketName, objectKey);
return new Promise((resolve, reject) => {
const chunks = [];
dataStream.on('data', chunk => chunks.push(chunk));
dataStream.on('end', () => resolve(Buffer.concat(chunks)));
dataStream.on('error', reject);
});
}
/**
* 単一オブジェクトを削除する
* @param {string} objectKey 削除するオブジェクトキー
* @returns {Promise<void>}
*/
async delete(objectKey) {
await this.minioClient.removeObject(this.bucketName, objectKey);
console.log('File deleted successfully.');
}
/**
* 署名付きURLを生成するGET or PUT
* @param {string} objectKey 対象のオブジェクトキー
* @param {number} processType 処理タイプ0=GET, 1=PUT
* @param {{ expiresIn: number }} options 有効期限
* @returns {Promise<string>} 発行された署名付きURL
*/
async getSignedUrl(objectKey, processType, options) {
if (!options.expiresIn) {
options.expiresIn = 60 * 60; // 1 hours
}
switch (processType) {
case ProcessType.GET:
return this.minioClient.presignedGetObject(this.bucketName, objectKey, options.expiresIn);
case ProcessType.PUT:
return this.minioClient.presignedPutObject(this.bucketName, objectKey, options.expiresIn);
default:
throw new Error('Invalid process type.');
}
}
}
module.exports = MinioStorage;

View File

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

View File

@ -1,11 +0,0 @@
ID,Name,Age,Email
1,User_1,26,user1@example.com
2,User_2,65,user2@example.com
3,User_3,39,user3@example.com
4,User_4,51,user4@example.com
5,User_5,25,user5@example.com
6,User_6,26,user6@example.com
7,User_7,69,user7@example.com
8,User_8,47,user8@example.com
9,User_9,40,user9@example.com
10,User_10,28,user10@example.com
1 ID Name Age Email
2 1 User_1 26 user1@example.com
3 2 User_2 65 user2@example.com
4 3 User_3 39 user3@example.com
5 4 User_4 51 user4@example.com
6 5 User_5 25 user5@example.com
7 6 User_6 26 user6@example.com
8 7 User_7 69 user7@example.com
9 8 User_8 47 user8@example.com
10 9 User_9 40 user9@example.com
11 10 User_10 28 user10@example.com

View File

@ -1,11 +0,0 @@
ID,Name,Age,Email
1,User_1,63,user1@example.com
2,User_2,46,user2@example.com
3,User_3,69,user3@example.com
4,User_4,62,user4@example.com
5,User_5,64,user5@example.com
6,User_6,52,user6@example.com
7,User_7,41,user7@example.com
8,User_8,64,user8@example.com
9,User_9,22,user9@example.com
10,User_10,62,user10@example.com
1 ID Name Age Email
2 1 User_1 63 user1@example.com
3 2 User_2 46 user2@example.com
4 3 User_3 69 user3@example.com
5 4 User_4 62 user4@example.com
6 5 User_5 64 user5@example.com
7 6 User_6 52 user6@example.com
8 7 User_7 41 user7@example.com
9 8 User_8 64 user8@example.com
10 9 User_9 22 user9@example.com
11 10 User_10 62 user10@example.com

Binary file not shown.

View File

@ -1,11 +0,0 @@
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
1 ID Name Age Email
2 1 User_1 44 user1@example.com
3 2 User_2 20 user2@example.com
4 3 User_3 67 user3@example.com
5 4 User_4 49 user4@example.com
6 5 User_5 56 user5@example.com
7 6 User_6 63 user6@example.com
8 7 User_7 31 user7@example.com
9 8 User_8 67 user8@example.com
10 9 User_9 41 user9@example.com
11 10 User_10 30 user10@example.com

View File

@ -1,11 +0,0 @@
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
1 ID Name Age Email
2 1 User_1 37 user1@example.com
3 2 User_2 30 user2@example.com
4 3 User_3 63 user3@example.com
5 4 User_4 42 user4@example.com
6 5 User_5 31 user5@example.com
7 6 User_6 63 user6@example.com
8 7 User_7 28 user7@example.com
9 8 User_8 20 user8@example.com
10 9 User_9 42 user9@example.com
11 10 User_10 69 user10@example.com

View File

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

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;
}
});

File diff suppressed because one or more lines are too long

View File

@ -1,21 +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>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>

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

@ -8,6 +8,6 @@
<body> <body>
<h1>JavaScript Develop Debug</h1> <h1>JavaScript Develop Debug</h1>
<p id="message">Open the console to see the output</p> <p id="message">Open the console to see the output</p>
<script src="assets/index.js"></script> <script src="index.js"></script>
</body> </body>
</html> </html>

View File

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

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();

View File

@ -1,73 +0,0 @@
#!/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, // 2069歳
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));

View File

@ -1,199 +0,0 @@
# MinIO 利用ガイド
## 概要
[MinIO](https://min.io/) は Amazon S3 互換のオブジェクトストレージサーバー。軽量かつシンプルな構成で、オンプレミスやクラウド上に S3 互換のストレージ環境を構築できる。
高可用構成やセキュリティ機能も備えており、小規模から大規模用途まで幅広く利用できる。本記事では MinIO の導入方法、仕様要件、ユースケース、基本的な利用方法についてまとめる。
---
## 1. インストール方法
### 1.1 Docker を使う場合
```bash
docker run -p 9000:9000 -p 9001:9001 \
--name minio \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=password123" \
-v /mnt/data:/data \
quay.io/minio/minio server /data --console-address ":9001"
```
- `9000`: S3互換API用ポート
- `9001`: Webコンソール用ポート
![mino-top.png](/mino-top.png)
---
### 1.2 Docker Compose を使う場合
```yaml
version: '3.8'
services:
minio:
image: quay.io/minio/minio
ports:
- "9000:9000"
- "9001:9001"
volumes:
- ./data:/data
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: password123
command: server /data --console-address ":9001"
restart: unless-stopped
```
起動コマンド:
```bash
docker compose up -d
```
---
### 1.3 バイナリから直接実行する場合Linux
```bash
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
sudo mv minio /usr/local/bin/
export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=password123
minio server /mnt/data --console-address ":9001"
```
---
## 2. 仕様要件
| 項目 | 推奨構成例 |
|--------------|------------------------------|
| OS | LinuxUbuntu 22.04 など) |
| CPU | 1コア以上最低限 |
| メモリ | 1GB 以上2GB 以上推奨) |
| ストレージ | SSD または HDD |
| 通信 | ポート 9000 / 9001開放 |
- HTTPS を使用する場合は TLS 証明書Let's Encrypt など)を導入
- リバースプロキシNginx/Caddyなど経由での公開も可能
---
## 3. ユースケース・機能
### 主なユースケース
- バックアップ保存
- Webアプリの画像・動画ストレージ
- ログデータ保管
- オンプレS3互換ストレージ
### 主な機能
- S3 互換API対応
- Webベース管理コンソール
- IAM風のユーザー・ポリシー制御
- TLS対応HTTPS通信
- サーバーサイド暗号化AES-256
- マルチテナント対応(認証連携含む)
- オブジェクトのバージョン管理(設定時)
---
## 4. 実際の使い方
サンプルコードでは以下を作成しております
1. バケット作成 : sample-data(readwrite)
2. ユーザー設定 : appuser:password123
### Webコンソールを利用する場合
#### Webコンソールにアクセス
ブラウザで `http://<ホスト>:9001` にアクセス
管理者アカウントでログイン(`MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD`
---
#### バケット作成・ファイルアップロード
- Web UI からバケット作成
- ローカルからファイルをアップロード可能
- バケット単位で「パブリックアクセス許可」も可能
### `mc` コマンドによる操作
#### インストール
```bash
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/
```
#### MinIOへの接続
```bash
mc alias set local http://localhost:9000 admin password123
```
#### 操作例
```bash
mc mb local/mybucket
mc cp ./example.jpg local/mybucket/
mc ls local/mybucket
```
### 各プログラム言語のライブラリから利用する場合
MinIOはAmazon S3互換のAPIを提供しているためS3対応の
クライアントライブラリを使用すればPythonやNode.jsから
簡単に接続・操作できます
* Python: `boto3`
* Node.js: `@aws-sdk/client-s3`
**接続時の注意点**
* リージョン
* 仮に指定しますが実際は無視される
* バージョニング/ライフサイクル/通知
* 一部のS3機能はMinIOでサポートされていない、または挙動が違う場合あり
* 署名付きURL
* boto3などで生成する署名付きURLも動作しますが、MinIOの設定によって挙動が異なることがある
### minioライブラリ(MinIO公式)
MinIO専用に最適化されていて、軽量・シンプルで扱いやすいのが特徴です。
S3互換APIを使ってはいますがAmazon純正のSDK@aws-sdk/*と違いMinIOのユースケースに特化してます
#### インストール方法
```sh
npm install minio
```
#### インストール方法
---
## 付録Tips
- リバースプロキシNginxを通して HTTPS 公開することで、ブラウザ互換性やセキュリティを向上できる
- Cloudflare などのCDNと組み合わせれば、簡易的なグローバル配信も可能
- アプリからは AWS SDK を使ってアクセス可能S3互換
---
## まとめ
MinIO は S3互換かつ軽量で、開発・検証・軽量サービスにおけるストレージニーズに柔軟に対応できる。Docker環境でも素早く構築可能なため、内部ツールやバックアップ用途として導入検討する価値がある。

View File

@ -1,20 +0,0 @@
const Minio = require('minio');
const minioClient = new Minio.Client({
endPoint: 'localhost',
port: 9000,
useSSL: false,
accessKey: 'appuser',
secretKey: 'password123'
});
const BUCKET_NAME = 'sample-data';
const OBJECT_NAME = 'sample2.csv';
minioClient.removeObject(BUCKET_NAME, OBJECT_NAME, (err) => {
if (err) {
return console.log("file delete error:", err);
}
console.log('File deleted successfully.');
});

View File

@ -1,19 +0,0 @@
const Minio = require('minio');
const minioClient = new Minio.Client({
endPoint: 'localhost',
port: 9000,
useSSL: false,
accessKey: 'appuser',
secretKey: 'password123'
});
const BUCKET_NAME = 'sample-data';
const OBJECT_NAMES = ['file1.csv', 'file2.csv'];
minioClient.removeObjects(BUCKET_NAME, OBJECT_NAMES, (err) => {
if (err) {
return console.log("files delete error:", err);
}
console.log('Files deleted successfully.');
});

View File

@ -1,35 +0,0 @@
/**
* MinIOに保存されているファイル一覧を取得する
*/
const Minio = require('minio');
const fs = require('fs');
const minioClient = new Minio.Client({
endPoint: 'localhost',
port: 9000,
useSSL: false,
accessKey: 'appuser',
secretKey: 'password123'
});
const BUCKET_NAME = 'sample-data';
const OBJECT_NAME = 'sample1.csv';
minioClient.getObject(BUCKET_NAME, OBJECT_NAME, (err, dataStream) => {
if (err) {
return console.log("file get error:", err);
}
const file = fs.createWriteStream('./sample1_direct.csv');
dataStream.pipe(file);
console.log('File downloaded successfully.');
dataStream.on('end', () => {
console.log('File Download File end');
});
dataStream.on('error', err => {
console.error('File Download Err:', err);
});
});

View File

@ -1,28 +0,0 @@
/**
* MinIOに保存されているファイル一覧を取得する
*/
const Minio = require('minio');
const minioClient = new Minio.Client({
endPoint: 'localhost',
port: 9000,
useSSL: false,
accessKey: 'appuser',
secretKey: 'password123'
});
const BUCKET_NAME = 'sample-data';
// listObjects はストリームなので順に読み取る必要があります
const stream = minioClient.listObjects(BUCKET_NAME, '', true); // true: 再帰的にすべて取得
stream.on('data', obj => {
console.log(`data ${obj.name} (${obj.size} bytes)`);
});
stream.on('end', () => {
console.log('Get File list end');
});
stream.on('error', err => {
console.error('Files get Err:', err);
});

View File

@ -1,27 +0,0 @@
/**
* MinIOに保存されているファイル一覧を非同期関数で取得する
*/
const Minio = require('minio');
const minioClient = new Minio.Client({
endPoint: 'localhost',
port: 9000,
useSSL: false,
accessKey: 'appuser',
secretKey: 'password123'
});
const BUCKET_NAME = 'sample-data';
function listAllObjects(bucket) {
return new Promise((resolve, reject) => {
const result = [];
const stream = minioClient.listObjects(bucket, '', true);
stream.on('data', obj => result.push(obj));
stream.on('end', () => resolve(result));
stream.on('error', reject);
});
}
const objects = await listAllObjects(BUCKET_NAME);
console.log(objects.map(obj => obj.name));

View File

@ -1,32 +0,0 @@
const Minio = require('minio');
const BUCKET_NAME = 'sample-data';
const minioClient = new Minio.Client({
endPoint: 'localhost',
port: 9000,
useSSL: false,
accessKey: 'appuser',
secretKey: 'password123'
});
// presigned GET URL を生成(有効期限: 1時間
minioClient.presignedGetObject(BUCKET_NAME, 'sample2.csv', 60 * 60, (err, url) => {
if (err) {
return console.log("file get signatureURL error:", err);
}
console.log('✅ Presigned GET URL:', url)
// fetchで取得してファイルに保存任意
// fetch(url)
// .then(res => res.buffer())
// .then(data => {
// // Bufferを使ってファイルに保存オプション
// import('fs').then(fs => {
// fs.writeFileSync('./downloaded_sample2.csv', data);
// console.log('File downloaded successfully.');
// });
// });
});

View File

@ -1,22 +0,0 @@
const Minio = require('minio');
const BUCKET_NAME = 'sample-data';
const minioClient = new Minio.Client({
endPoint: 'localhost',
port: 9000,
useSSL: false,
accessKey: 'appuser',
secretKey: 'password123'
});
// ファイルアップロード(src/data/sample1.csv)
minioClient.fPutObject(BUCKET_NAME, 'sample1.csv', './src/data/sample1.csv', function(err, etag) {
if (err){
return console.log("file upload error:", err);
}
console.log('File uploaded successfully.',etag);
});

View File

@ -1,30 +0,0 @@
const Minio = require('minio');
const BUCKET_NAME = 'sample-data';
const minioClient = new Minio.Client({
endPoint: 'localhost',
port: 9000,
useSSL: false,
accessKey: 'appuser',
secretKey: 'password123'
});
// ファイルアップロード(src/data/sample1.csv)
minioClient.presignedPutObject(BUCKET_NAME, 'sample2.csv',60 * 60, function(err, presignedUrl) {
if (err){
return console.log("file upload error:", err);
}
console.log('File uploaded URL:',presignedUrl);
// ダウンロードしたURLにPUTリクエストを送信する
});
// curl -X PUT -T .\src\data\sample2.csv "presignedUrl"
// const fileData = await fs.readFile(filePath);
// const res = await fetch(uploadUrl, {
// method: 'PUT',
// body: fileData,
// headers: {
// 'Content-Type': 'text/csv'
// }
// });

View File

@ -1,89 +0,0 @@
#!/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));

View File

@ -1,32 +0,0 @@
#!/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();

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

View File

@ -1,75 +0,0 @@
/**
* S3 / GCS / MinIO
*/
export enum ProcessType {
GET = 0,
PUT = 1
}
export interface IStorage {
/**
*
* @param filePath
* @param objectKey
*/
uploadFile: (filePath: string, objectKey: string) => Promise<void>;
/**
*
* @param fileData
* @param objectKey
* @param contentType Content-Type application/octet-stream
*/
uploadContents?: (
fileData: Buffer | string,
objectKey: string,
contentType?: string
) => Promise<void>;
/**
*
* @param objectKey
* @param destinationPath
* @returns Buffer
*/
downloadFile(objectKey: string, destinationPath: string): Promise<void>;
/**
*
* @param objectKey
* @returns Buffer
*/
downloadContents?(objectKey: string): Promise<Buffer>;
/**
*
* @param objectKey
*/
delete(objectKey: string): Promise<void>;
/**
*
* @param objectKeys
*/
deleteMany?(objectKeys: string[]): Promise<void>;
/**
*
* @param prefix
* @returns
*/
getFileList(prefix?: string): Promise<string[]>;
/**
* URLを発行する
* @param objectKey
* @param processType : 0 = GET, 1 = PUT
* @param options
* @returns URL
*/
getSignedUrl(
objectKey: string,
processType: ProcessType,
options: { expiresIn: number }
): Promise<string>;
}