fs-object-storage 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +7 -4
- package/README.md +23 -23
- package/docs/technical/api-reference.md +312 -0
- package/docs/technical/architecture.md +179 -0
- package/docs/technical/usage-examples.md +394 -0
- package/package.json +1 -1
- package/quick-test.js +9 -8
- package/samples/{fs-minio-test.js → fs-object-storage-test.js} +20 -24
- package/src/index.d.ts +4 -4
- package/src/index.js +4 -4
- package/src/lib/ErrorHandler.js +1 -1
- package/src/lib/{FsMinioClient.js → ObjectStorage.js} +4 -4
- package/src/lib/PathConverter.js +97 -32
- package/test-package.json +1 -1
- package/unit-tests.js +5 -5
|
@@ -29,13 +29,16 @@ jobs:
|
|
|
29
29
|
publish-npm:
|
|
30
30
|
needs: build
|
|
31
31
|
runs-on: ubuntu-latest
|
|
32
|
+
permissions:
|
|
33
|
+
id-token: write # OIDC認証に必須
|
|
34
|
+
contents: read
|
|
32
35
|
steps:
|
|
33
36
|
- uses: actions/checkout@v4
|
|
34
37
|
- uses: actions/setup-node@v4
|
|
35
38
|
with:
|
|
36
|
-
node-version: '
|
|
39
|
+
node-version: '20.x'
|
|
37
40
|
registry-url: https://registry.npmjs.org/
|
|
41
|
+
- run: npm install -g npm@latest
|
|
38
42
|
- run: npm ci
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
|
43
|
+
- name: Publish to NPM with Trusted Publishing
|
|
44
|
+
run: npm publish --access public
|
package/README.md
CHANGED
|
@@ -24,10 +24,10 @@ npm install fs-object-storage
|
|
|
24
24
|
### 基本的な使用方法
|
|
25
25
|
|
|
26
26
|
```javascript
|
|
27
|
-
import {
|
|
27
|
+
import { ObjectStorage } from 'fs-object-storage';
|
|
28
28
|
|
|
29
29
|
// MinIO/S3クライアントの設定
|
|
30
|
-
const
|
|
30
|
+
const fs = new ObjectStorage({
|
|
31
31
|
endPoint: 'localhost',
|
|
32
32
|
port: 9000,
|
|
33
33
|
useSSL: false,
|
|
@@ -38,18 +38,18 @@ const client = new FsMinioClient({
|
|
|
38
38
|
// ファイル操作(fs互換)
|
|
39
39
|
try {
|
|
40
40
|
// ファイル書き込み
|
|
41
|
-
await
|
|
41
|
+
await fs.writeFile('/mybucket/path/to/file.txt', 'Hello, World!');
|
|
42
42
|
|
|
43
43
|
// ファイル読み込み
|
|
44
|
-
const data = await
|
|
44
|
+
const data = await fs.readFile('/mybucket/path/to/file.txt', 'utf8');
|
|
45
45
|
console.log(data); // "Hello, World!"
|
|
46
46
|
|
|
47
47
|
// ファイル存在確認
|
|
48
|
-
const exists = await
|
|
48
|
+
const exists = await fs.exists('/mybucket/path/to/file.txt');
|
|
49
49
|
console.log(exists); // true
|
|
50
50
|
|
|
51
51
|
// ディレクトリ一覧
|
|
52
|
-
const files = await
|
|
52
|
+
const files = await fs.readdir('/mybucket/path');
|
|
53
53
|
console.log(files); // ['to/']
|
|
54
54
|
|
|
55
55
|
} catch (error) {
|
|
@@ -64,11 +64,11 @@ import fs from 'fs';
|
|
|
64
64
|
|
|
65
65
|
// 大容量ファイルのアップロード
|
|
66
66
|
const readStream = fs.createReadStream('./large-file.zip');
|
|
67
|
-
const writeStream = await
|
|
67
|
+
const writeStream = await fs.createWriteStream('/mybucket/uploads/large-file.zip');
|
|
68
68
|
readStream.pipe(writeStream);
|
|
69
69
|
|
|
70
70
|
// ダウンロードストリーム
|
|
71
|
-
const downloadStream = await
|
|
71
|
+
const downloadStream = await fs.createReadStream('/mybucket/uploads/large-file.zip');
|
|
72
72
|
const localWriteStream = fs.createWriteStream('./downloaded-file.zip');
|
|
73
73
|
downloadStream.pipe(localWriteStream);
|
|
74
74
|
```
|
|
@@ -111,7 +111,7 @@ npm run test:all
|
|
|
111
111
|
### コンストラクタ
|
|
112
112
|
|
|
113
113
|
```javascript
|
|
114
|
-
new
|
|
114
|
+
new ObjectStorage(options)
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
**options**:
|
|
@@ -127,28 +127,28 @@ new FsMinioClient(options)
|
|
|
127
127
|
ファイルを読み込みます。
|
|
128
128
|
|
|
129
129
|
```javascript
|
|
130
|
-
const data = await
|
|
130
|
+
const data = await fs.readFile('/bucket/file.txt', 'utf8');
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
#### `writeFile(path, data, options?)`
|
|
134
134
|
ファイルを書き込みます。
|
|
135
135
|
|
|
136
136
|
```javascript
|
|
137
|
-
await
|
|
137
|
+
await fs.writeFile('/bucket/file.txt', 'データ');
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
#### `exists(path)`
|
|
141
141
|
ファイル/ディレクトリの存在を確認します。
|
|
142
142
|
|
|
143
143
|
```javascript
|
|
144
|
-
const exists = await
|
|
144
|
+
const exists = await fs.exists('/bucket/file.txt');
|
|
145
145
|
```
|
|
146
146
|
|
|
147
147
|
#### `stat(path)`
|
|
148
148
|
ファイル/ディレクトリの統計情報を取得します。
|
|
149
149
|
|
|
150
150
|
```javascript
|
|
151
|
-
const stats = await
|
|
151
|
+
const stats = await fs.stat('/bucket/file.txt');
|
|
152
152
|
console.log(stats.size, stats.isFile(), stats.isDirectory());
|
|
153
153
|
```
|
|
154
154
|
|
|
@@ -156,14 +156,14 @@ console.log(stats.size, stats.isFile(), stats.isDirectory());
|
|
|
156
156
|
ファイルを削除します。
|
|
157
157
|
|
|
158
158
|
```javascript
|
|
159
|
-
await
|
|
159
|
+
await fs.unlink('/bucket/file.txt');
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
#### `copyFile(src, dest)`
|
|
163
163
|
ファイルをコピーします。
|
|
164
164
|
|
|
165
165
|
```javascript
|
|
166
|
-
await
|
|
166
|
+
await fs.copyFile('/bucket/src.txt', '/bucket/dest.txt');
|
|
167
167
|
```
|
|
168
168
|
|
|
169
169
|
### ディレクトリ操作メソッド
|
|
@@ -172,21 +172,21 @@ await client.copyFile('/bucket/src.txt', '/bucket/dest.txt');
|
|
|
172
172
|
ディレクトリの内容を一覧します。
|
|
173
173
|
|
|
174
174
|
```javascript
|
|
175
|
-
const files = await
|
|
175
|
+
const files = await fs.readdir('/bucket/directory');
|
|
176
176
|
```
|
|
177
177
|
|
|
178
178
|
#### `mkdir(path, options?)`
|
|
179
179
|
ディレクトリを作成します。
|
|
180
180
|
|
|
181
181
|
```javascript
|
|
182
|
-
await
|
|
182
|
+
await fs.mkdir('/bucket/new-directory', { recursive: true });
|
|
183
183
|
```
|
|
184
184
|
|
|
185
185
|
#### `rmdir(path)`
|
|
186
186
|
空のディレクトリを削除します。
|
|
187
187
|
|
|
188
188
|
```javascript
|
|
189
|
-
await
|
|
189
|
+
await fs.rmdir('/bucket/empty-directory');
|
|
190
190
|
```
|
|
191
191
|
|
|
192
192
|
### ストリーム操作メソッド
|
|
@@ -195,14 +195,14 @@ await client.rmdir('/bucket/empty-directory');
|
|
|
195
195
|
読み込みストリームを作成します。
|
|
196
196
|
|
|
197
197
|
```javascript
|
|
198
|
-
const stream = await
|
|
198
|
+
const stream = await fs.createReadStream('/bucket/file.txt');
|
|
199
199
|
```
|
|
200
200
|
|
|
201
201
|
#### `createWriteStream(path)`
|
|
202
202
|
書き込みストリームを作成します。
|
|
203
203
|
|
|
204
204
|
```javascript
|
|
205
|
-
const stream = await
|
|
205
|
+
const stream = await fs.createWriteStream('/bucket/file.txt');
|
|
206
206
|
```
|
|
207
207
|
|
|
208
208
|
## 🗺️ パス形式
|
|
@@ -223,7 +223,7 @@ MinIOのエラーは自動的にfs互換のエラーコードに変換されま
|
|
|
223
223
|
|
|
224
224
|
```javascript
|
|
225
225
|
try {
|
|
226
|
-
await
|
|
226
|
+
await fs.readFile('/bucket/nonexistent.txt');
|
|
227
227
|
} catch (error) {
|
|
228
228
|
console.log(error.code); // 'ENOENT'
|
|
229
229
|
console.log(error.errno); // -2
|
|
@@ -252,7 +252,7 @@ try {
|
|
|
252
252
|
|
|
253
253
|
本ライブラリは以下のコンポーネントで構成されています:
|
|
254
254
|
|
|
255
|
-
- **
|
|
255
|
+
- **ObjectStorage**: メインのfs互換クライアント
|
|
256
256
|
- **ErrorHandler**: MinIO→fs エラー変換
|
|
257
257
|
- **PathConverter**: ファイルパス⇔バケット/キー変換
|
|
258
258
|
- **StreamConverter**: ストリーム/データ形式変換
|
|
@@ -284,4 +284,4 @@ MIT
|
|
|
284
284
|
|
|
285
285
|
- **ドキュメント**: [`docs/`](./docs/) フォルダ
|
|
286
286
|
- **GitHub Issues**: バグ報告・機能要求
|
|
287
|
-
- **サンプルコード**: [`samples/`](./samples/) フォルダ
|
|
287
|
+
- **サンプルコード**: [`samples/`](./samples/) フォルダ
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# fs-object-storage API リファレンス
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
`fs-object-storage`は、Node.js標準の`fs`モジュールと互換性のあるAPIを提供しながら、バックエンドでMinIO/S3オブジェクトストレージを使用するライブラリです。
|
|
5
|
+
|
|
6
|
+
## インストール・セットアップ
|
|
7
|
+
|
|
8
|
+
### 依存関係
|
|
9
|
+
```bash
|
|
10
|
+
npm install minio
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### 基本的な使用方法
|
|
14
|
+
```javascript
|
|
15
|
+
import { ObjectStorage } from './src/index.js';
|
|
16
|
+
|
|
17
|
+
const fs = new ObjectStorage({
|
|
18
|
+
endpoint: 'localhost:9000',
|
|
19
|
+
accessKey: 'minioadmin',
|
|
20
|
+
secretKey: 'minioadmin123',
|
|
21
|
+
bucket: 'my-app-bucket',
|
|
22
|
+
useSSL: false,
|
|
23
|
+
prefix: 'app-data' // オプション:全てのキーにプレフィックスを追加
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// 初期化(バケット作成など)
|
|
27
|
+
await fs.initialize();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## コンストラクター
|
|
31
|
+
|
|
32
|
+
### `new ObjectStorage(options)`
|
|
33
|
+
|
|
34
|
+
MinIOクライアントインスタンスを作成します。
|
|
35
|
+
|
|
36
|
+
**パラメーター:**
|
|
37
|
+
- `options` (Object) - 設定オプション
|
|
38
|
+
- `endpoint` (string) - MinIOエンドポイント (例: 'localhost:9000')
|
|
39
|
+
- `accessKey` (string) - アクセスキー
|
|
40
|
+
- `secretKey` (string) - シークレットキー
|
|
41
|
+
- `bucket` (string) - 使用するバケット名
|
|
42
|
+
- `useSSL` (boolean, optional) - SSL使用フラグ(デフォルト: false)
|
|
43
|
+
- `region` (string, optional) - リージョン(デフォルト: 'us-east-1')
|
|
44
|
+
- `prefix` (string, optional) - 全キーに追加するプレフィックス
|
|
45
|
+
|
|
46
|
+
## ファイル操作メソッド
|
|
47
|
+
|
|
48
|
+
### `readFile(filePath, options)`
|
|
49
|
+
|
|
50
|
+
ファイルの内容を読み取ります。
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
// テキストファイルを読む
|
|
54
|
+
const content = await fs.readFile('/data/hello.txt', 'utf8');
|
|
55
|
+
|
|
56
|
+
// バイナリファイルを読む
|
|
57
|
+
const buffer = await fs.readFile('/images/photo.jpg');
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**パラメーター:**
|
|
61
|
+
- `filePath` (string) - ファイルパス
|
|
62
|
+
- `options` (string|Object, optional) - エンコーディングまたはオプション
|
|
63
|
+
- `encoding` (string) - テキストエンコーディング
|
|
64
|
+
|
|
65
|
+
**戻り値:** `Promise<Buffer|string>` - ファイル内容
|
|
66
|
+
|
|
67
|
+
### `writeFile(filePath, data, options)`
|
|
68
|
+
|
|
69
|
+
ファイルにデータを書き込みます。
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
// テキストファイルを書く
|
|
73
|
+
await fs.writeFile('/data/hello.txt', 'Hello World!', 'utf8');
|
|
74
|
+
|
|
75
|
+
// バイナリファイルを書く
|
|
76
|
+
const buffer = Buffer.from([0x89, 0x50, 0x4E, 0x47]);
|
|
77
|
+
await fs.writeFile('/images/test.png', buffer);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**パラメーター:**
|
|
81
|
+
- `filePath` (string) - ファイルパス
|
|
82
|
+
- `data` (string|Buffer|Uint8Array) - 書き込むデータ
|
|
83
|
+
- `options` (string|Object, optional) - エンコーディングまたはオプション
|
|
84
|
+
|
|
85
|
+
**戻り値:** `Promise<void>`
|
|
86
|
+
|
|
87
|
+
### `exists(filePath)`
|
|
88
|
+
|
|
89
|
+
ファイルの存在を確認します。
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
const fileExists = await fs.exists('/data/hello.txt');
|
|
93
|
+
console.log(fileExists); // true または false
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**パラメーター:**
|
|
97
|
+
- `filePath` (string) - ファイルパス
|
|
98
|
+
|
|
99
|
+
**戻り値:** `Promise<boolean>` - ファイル存在フラグ
|
|
100
|
+
|
|
101
|
+
### `stat(filePath)`
|
|
102
|
+
|
|
103
|
+
ファイルの統計情報を取得します。
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
const stats = await fs.stat('/data/hello.txt');
|
|
107
|
+
console.log('ファイルサイズ:', stats.size);
|
|
108
|
+
console.log('更新日時:', stats.mtime);
|
|
109
|
+
console.log('ファイルかどうか:', stats.isFile());
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**パラメーター:**
|
|
113
|
+
- `filePath` (string) - ファイルパス
|
|
114
|
+
|
|
115
|
+
**戻り値:** `Promise<Object>` - fs.Stats風オブジェクト
|
|
116
|
+
|
|
117
|
+
### `unlink(filePath)`
|
|
118
|
+
|
|
119
|
+
ファイルを削除します。
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
await fs.unlink('/data/old-file.txt');
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**パラメーター:**
|
|
126
|
+
- `filePath` (string) - ファイルパス
|
|
127
|
+
|
|
128
|
+
**戻り値:** `Promise<void>`
|
|
129
|
+
|
|
130
|
+
## ディレクトリ操作メソッド
|
|
131
|
+
|
|
132
|
+
### `readdir(dirPath, options)`
|
|
133
|
+
|
|
134
|
+
ディレクトリの内容を一覧表示します。
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
// ファイル名の配列を取得
|
|
138
|
+
const files = await fs.readdir('/data');
|
|
139
|
+
|
|
140
|
+
// Direntオブジェクトの配列を取得
|
|
141
|
+
const entries = await fs.readdir('/data', { withFileTypes: true });
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**パラメーター:**
|
|
145
|
+
- `dirPath` (string) - ディレクトリパス
|
|
146
|
+
- `options` (Object, optional) - オプション
|
|
147
|
+
- `withFileTypes` (boolean) - Direntオブジェクトを返すかどうか
|
|
148
|
+
|
|
149
|
+
**戻り値:** `Promise<string[]|Object[]>` - ファイル名またはDirentオブジェクトの配列
|
|
150
|
+
|
|
151
|
+
### `mkdir(dirPath, options)`
|
|
152
|
+
|
|
153
|
+
ディレクトリを作成します。
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
// 単一ディレクトリ作成
|
|
157
|
+
await fs.mkdir('/new-folder');
|
|
158
|
+
|
|
159
|
+
// 再帰的作成(親ディレクトリも作成)
|
|
160
|
+
await fs.mkdir('/deep/nested/folder', { recursive: true });
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**パラメーター:**
|
|
164
|
+
- `dirPath` (string) - ディレクトリパス
|
|
165
|
+
- `options` (Object, optional) - オプション
|
|
166
|
+
- `recursive` (boolean) - 親ディレクトリも作成するかどうか
|
|
167
|
+
|
|
168
|
+
**戻り値:** `Promise<void>`
|
|
169
|
+
|
|
170
|
+
### `rmdir(dirPath)`
|
|
171
|
+
|
|
172
|
+
空のディレクトリを削除します。
|
|
173
|
+
|
|
174
|
+
```javascript
|
|
175
|
+
await fs.rmdir('/empty-folder');
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**パラメーター:**
|
|
179
|
+
- `dirPath` (string) - ディレクトリパス
|
|
180
|
+
|
|
181
|
+
**戻り値:** `Promise<void>`
|
|
182
|
+
|
|
183
|
+
## ストリーム操作メソッド
|
|
184
|
+
|
|
185
|
+
### `createReadStream(filePath, options)`
|
|
186
|
+
|
|
187
|
+
読み取りストリームを作成します。
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
const readStream = await fs.createReadStream('/large-file.txt');
|
|
191
|
+
|
|
192
|
+
readStream.on('data', (chunk) => {
|
|
193
|
+
console.log('受信:', chunk.length, 'バイト');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
readStream.on('end', () => {
|
|
197
|
+
console.log('読み取り完了');
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**パラメーター:**
|
|
202
|
+
- `filePath` (string) - ファイルパス
|
|
203
|
+
- `options` (Object, optional) - ストリームオプション
|
|
204
|
+
|
|
205
|
+
**戻り値:** `Promise<Readable>` - 読み取りストリーム
|
|
206
|
+
|
|
207
|
+
### `createWriteStream(filePath, options)`
|
|
208
|
+
|
|
209
|
+
書き込みストリームを作成します。
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
const writeStream = fs.createWriteStream('/output.txt');
|
|
213
|
+
|
|
214
|
+
writeStream.write('Hello ');
|
|
215
|
+
writeStream.write('World!');
|
|
216
|
+
writeStream.end();
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**パラメーター:**
|
|
220
|
+
- `filePath` (string) - ファイルパス
|
|
221
|
+
- `options` (Object, optional) - ストリームオプション
|
|
222
|
+
|
|
223
|
+
**戻り値:** `Writable` - 書き込みストリーム
|
|
224
|
+
|
|
225
|
+
## 高度な操作
|
|
226
|
+
|
|
227
|
+
### `copyFile(srcPath, destPath)`
|
|
228
|
+
|
|
229
|
+
ファイルをコピーします。
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
await fs.copyFile('/source.txt', '/destination.txt');
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**パラメーター:**
|
|
236
|
+
- `srcPath` (string) - コピー元ファイルパス
|
|
237
|
+
- `destPath` (string) - コピー先ファイルパス
|
|
238
|
+
|
|
239
|
+
**戻り値:** `Promise<void>`
|
|
240
|
+
|
|
241
|
+
## エラーハンドリング
|
|
242
|
+
|
|
243
|
+
ライブラリは標準的なNode.js `fs`モジュールと同じエラーコードを使用します:
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
try {
|
|
247
|
+
await fs.readFile('/nonexistent.txt');
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (error.code === 'ENOENT') {
|
|
250
|
+
console.log('ファイルが見つかりません');
|
|
251
|
+
} else if (error.code === 'EACCES') {
|
|
252
|
+
console.log('アクセス権限がありません');
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### 主要なエラーコード
|
|
258
|
+
- `ENOENT` - ファイル/ディレクトリが存在しない
|
|
259
|
+
- `EACCES` - アクセス権限拒否
|
|
260
|
+
- `EEXIST` - ファイル/ディレクトリが既に存在
|
|
261
|
+
- `ENOTDIR` - ディレクトリではない
|
|
262
|
+
- `ENOTEMPTY` - ディレクトリが空でない
|
|
263
|
+
|
|
264
|
+
## パス変換について
|
|
265
|
+
|
|
266
|
+
ファイルシステムパスは自動的にMinIOオブジェクトキーに変換されます:
|
|
267
|
+
|
|
268
|
+
```javascript
|
|
269
|
+
// ファイルシステムパス → MinIOキー
|
|
270
|
+
'/data/users/123/profile.json' → 'data/users/123/profile.json'
|
|
271
|
+
|
|
272
|
+
// プレフィックス付きの場合
|
|
273
|
+
// prefix: 'app-data'
|
|
274
|
+
'/data/file.txt' → 'app-data/data/file.txt'
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## 仮想ディレクトリ
|
|
278
|
+
|
|
279
|
+
MinIOにはディレクトリの概念がないため、プレフィックスベースの仮想ディレクトリを実装しています:
|
|
280
|
+
|
|
281
|
+
- ディレクトリマーカー(空のオブジェクト)を使用
|
|
282
|
+
- `readdir()`は共通プレフィックスでオブジェクトを検索
|
|
283
|
+
- `mkdir()`はディレクトリマーカーオブジェクトを作成
|
|
284
|
+
|
|
285
|
+
## パフォーマンス考慮事項
|
|
286
|
+
|
|
287
|
+
- 大きなファイルにはストリームAPIを使用することを推奨
|
|
288
|
+
- 同期APIは提供していません(全て非同期)
|
|
289
|
+
- バッチ操作は個別に実装する必要があります
|
|
290
|
+
|
|
291
|
+
## トラブルシューティング
|
|
292
|
+
|
|
293
|
+
### よくある問題
|
|
294
|
+
|
|
295
|
+
1. **接続エラー**
|
|
296
|
+
```javascript
|
|
297
|
+
// 接続設定を確認
|
|
298
|
+
console.log('MinIOエンドポイント:', fs.endpoint);
|
|
299
|
+
// MinIOサーバーが起動しているか確認
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
2. **認証エラー**
|
|
303
|
+
```javascript
|
|
304
|
+
// アクセスキー・シークレットキーを確認
|
|
305
|
+
// MinIOコンソールで権限を確認
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
3. **バケット存在エラー**
|
|
309
|
+
```javascript
|
|
310
|
+
// initialize()を呼び出してバケットを作成
|
|
311
|
+
await fs.initialize();
|
|
312
|
+
```
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# fs-object-storage アーキテクチャ設計
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
Node.js fsモジュール互換のAPIを提供しながら、バックエンドでMinIO/S3オブジェクトストレージを使用するライブラリ。
|
|
5
|
+
|
|
6
|
+
## アーキテクチャ
|
|
7
|
+
|
|
8
|
+
### レイヤー構造
|
|
9
|
+
```
|
|
10
|
+
┌─────────────────────────────────────┐
|
|
11
|
+
│ fs 互換 API レイヤー │
|
|
12
|
+
│ (readFile, writeFile, exists, etc.) │
|
|
13
|
+
├─────────────────────────────────────┤
|
|
14
|
+
│ パス変換レイヤー │
|
|
15
|
+
│ (/path/to/file → bucket/key) │
|
|
16
|
+
├─────────────────────────────────────┤
|
|
17
|
+
│ ストリーム変換レイヤー │
|
|
18
|
+
│ (Buffer/String ↔ MinIO Stream) │
|
|
19
|
+
├─────────────────────────────────────┤
|
|
20
|
+
│ エラー変換レイヤー │
|
|
21
|
+
│ (MinIO Error → fs Error) │
|
|
22
|
+
├─────────────────────────────────────┤
|
|
23
|
+
│ MinIO クライアント │
|
|
24
|
+
│ (Low-level object operations) │
|
|
25
|
+
└─────────────────────────────────────┘
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 主要コンポーネント
|
|
29
|
+
|
|
30
|
+
### 1. ObjectStorage
|
|
31
|
+
- メインのクライアントクラス
|
|
32
|
+
- fs互換APIの提供
|
|
33
|
+
- 設定管理(bucket名、接続情報等)
|
|
34
|
+
|
|
35
|
+
### 2. PathConverter
|
|
36
|
+
- ファイルシステムパス → MinIOオブジェクトキー変換
|
|
37
|
+
- 仮想ディレクトリ実装(プレフィックスベース)
|
|
38
|
+
- パス正規化
|
|
39
|
+
|
|
40
|
+
### 3. StreamConverter
|
|
41
|
+
- Buffer/String → MinIO readable stream
|
|
42
|
+
- MinIO readable stream → Buffer/String
|
|
43
|
+
- 非同期ストリーム処理
|
|
44
|
+
|
|
45
|
+
### 4. ErrorHandler
|
|
46
|
+
- MinIOエラー → fs-style エラー変換
|
|
47
|
+
- 適切なerror.codeの設定
|
|
48
|
+
- エラーメッセージの正規化
|
|
49
|
+
|
|
50
|
+
### 5. MetadataManager
|
|
51
|
+
- ファイルメタデータの管理
|
|
52
|
+
- 仮想ディレクトリ情報
|
|
53
|
+
- タイムスタンプ、サイズ等の stat 情報
|
|
54
|
+
|
|
55
|
+
## API設計
|
|
56
|
+
|
|
57
|
+
### 基本原則
|
|
58
|
+
- fs モジュールとの完全互換性
|
|
59
|
+
- 同期/非同期両方のAPIサポート
|
|
60
|
+
- Promise ベースの実装
|
|
61
|
+
- TypeScript サポート
|
|
62
|
+
|
|
63
|
+
### 実装対象メソッド(優先度順)
|
|
64
|
+
|
|
65
|
+
#### Phase 1: 基本ファイル操作
|
|
66
|
+
- [x] `readFile(path, options)` - ファイル読み込み
|
|
67
|
+
- [x] `writeFile(path, data, options)` - ファイル書き込み
|
|
68
|
+
- [x] `exists(path)` - ファイル存在確認
|
|
69
|
+
- [x] `stat(path)` - ファイル情報取得
|
|
70
|
+
- [x] `unlink(path)` - ファイル削除
|
|
71
|
+
|
|
72
|
+
#### Phase 2: ディレクトリ操作
|
|
73
|
+
- [ ] `readdir(path)` - ディレクトリ一覧
|
|
74
|
+
- [ ] `mkdir(path)` - ディレクトリ作成
|
|
75
|
+
- [ ] `rmdir(path)` - ディレクトリ削除
|
|
76
|
+
|
|
77
|
+
#### Phase 3: ストリーム操作
|
|
78
|
+
- [ ] `createReadStream(path)` - 読み込みストリーム
|
|
79
|
+
- [ ] `createWriteStream(path)` - 書き込みストリーム
|
|
80
|
+
|
|
81
|
+
#### Phase 4: 高度な操作
|
|
82
|
+
- [ ] `copyFile(src, dest)` - ファイルコピー
|
|
83
|
+
- [ ] `rename(oldPath, newPath)` - ファイル移動/リネーム
|
|
84
|
+
|
|
85
|
+
## パス変換仕様
|
|
86
|
+
|
|
87
|
+
### ファイルシステムパス → MinIOキー
|
|
88
|
+
```javascript
|
|
89
|
+
// 例:
|
|
90
|
+
'/data/users/123/profile.json'
|
|
91
|
+
→ bucket: 'myapp', key: 'data/users/123/profile.json'
|
|
92
|
+
|
|
93
|
+
'/images/avatar.png'
|
|
94
|
+
→ bucket: 'myapp', key: 'images/avatar.png'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 仮想ディレクトリ実装
|
|
98
|
+
- MinIOにディレクトリ概念はないため、プレフィックスベースで実装
|
|
99
|
+
- 空の「ディレクトリマーカー」オブジェクトを作成
|
|
100
|
+
- readdir では共通プレフィックスでオブジェクト一覧を取得
|
|
101
|
+
|
|
102
|
+
## エラーハンドリング
|
|
103
|
+
|
|
104
|
+
### MinIOエラー → fsエラー変換マッピング
|
|
105
|
+
```javascript
|
|
106
|
+
const errorMapping = {
|
|
107
|
+
'NoSuchKey': { code: 'ENOENT', errno: -2 },
|
|
108
|
+
'AccessDenied': { code: 'EACCES', errno: -13 },
|
|
109
|
+
'BucketNotFound': { code: 'ENOENT', errno: -2 },
|
|
110
|
+
'InvalidBucketName': { code: 'EINVAL', errno: -22 },
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 設定例
|
|
115
|
+
```javascript
|
|
116
|
+
const fs = new ObjectStorage({
|
|
117
|
+
endpoint: 'localhost:9000',
|
|
118
|
+
accessKey: 'minioadmin',
|
|
119
|
+
secretKey: 'minioadmin',
|
|
120
|
+
bucket: 'myapp',
|
|
121
|
+
useSSL: false
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// fs互換API
|
|
125
|
+
await fs.writeFile('/data/test.txt', 'Hello World');
|
|
126
|
+
const content = await fs.readFile('/data/test.txt', 'utf8');
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 実装フェーズ計画
|
|
130
|
+
|
|
131
|
+
### Phase 1: 基本実装 (目標: 2-3日)
|
|
132
|
+
1. プロジェクト構造セットアップ
|
|
133
|
+
2. FsMinioClient基本クラス作成
|
|
134
|
+
3. 基本ファイル操作(read/write/exists/stat/unlink)実装
|
|
135
|
+
4. 基本的なエラーハンドリング
|
|
136
|
+
|
|
137
|
+
### Phase 2: ディレクトリ機能 (目標: 1-2日)
|
|
138
|
+
1. PathConverter実装
|
|
139
|
+
2. 仮想ディレクトリ機能
|
|
140
|
+
3. readdir, mkdir, rmdir実装
|
|
141
|
+
|
|
142
|
+
### Phase 3: ストリーム機能 (目標: 1-2日)
|
|
143
|
+
1. StreamConverter実装
|
|
144
|
+
2. createReadStream, createWriteStream実装
|
|
145
|
+
3. 大きなファイル対応
|
|
146
|
+
|
|
147
|
+
### Phase 4: 完成 (目標: 1日)
|
|
148
|
+
1. 高度な機能(copy, rename等)
|
|
149
|
+
2. TypeScript型定義
|
|
150
|
+
3. 詳細テスト
|
|
151
|
+
4. ドキュメント整備
|
|
152
|
+
|
|
153
|
+
## テスト戦略
|
|
154
|
+
|
|
155
|
+
### 単体テスト
|
|
156
|
+
- 各コンポーネントの独立テスト
|
|
157
|
+
- モックを使用したMinIOクライアント分離
|
|
158
|
+
|
|
159
|
+
### 統合テスト
|
|
160
|
+
- 実際のMinIOコンテナを使用
|
|
161
|
+
- End-to-endテストシナリオ
|
|
162
|
+
|
|
163
|
+
### パフォーマンステスト
|
|
164
|
+
- 大きなファイルの読み書き
|
|
165
|
+
- 同時接続数の確認
|
|
166
|
+
- メモリ使用量の監視
|
|
167
|
+
|
|
168
|
+
### `new ObjectStorage(options)`
|
|
169
|
+
```javascript
|
|
170
|
+
const fs = new ObjectStorage({
|
|
171
|
+
endpoint: 'localhost:9000',
|
|
172
|
+
accessKey: 'minioadmin',
|
|
173
|
+
secretKey: 'minioadmin',
|
|
174
|
+
bucket: 'myapp',
|
|
175
|
+
useSSL: false
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// 以降のclientをfsに統一
|
|
179
|
+
```
|