fs-object-storage 1.0.0 → 1.0.1
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/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
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
# fs-object-storage 使用例
|
|
2
|
+
|
|
3
|
+
このドキュメントでは、`fs-object-storage`ライブラリの実際的な使用例を紹介します。
|
|
4
|
+
|
|
5
|
+
## 1. 基本セットアップ
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import { ObjectStorage } from 'fs-object-storage';
|
|
9
|
+
|
|
10
|
+
// クライアント作成
|
|
11
|
+
const fs = new ObjectStorage({
|
|
12
|
+
endpoint: 'localhost:9000',
|
|
13
|
+
accessKey: 'minioadmin',
|
|
14
|
+
secretKey: 'minioadmin123',
|
|
15
|
+
bucket: 'my-app-storage',
|
|
16
|
+
useSSL: false,
|
|
17
|
+
prefix: 'app-data'
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// 初期化
|
|
21
|
+
await fs.initialize();
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 2. ファイル管理システム
|
|
25
|
+
|
|
26
|
+
### ファイルアップロード
|
|
27
|
+
```javascript
|
|
28
|
+
async function uploadFile(filePath, fileContent) {
|
|
29
|
+
try {
|
|
30
|
+
await fs.writeFile(filePath, fileContent);
|
|
31
|
+
console.log(`ファイル ${filePath} をアップロードしました`);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error('アップロードエラー:', error.message);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// テキストファイル
|
|
38
|
+
await uploadFile('/documents/readme.txt', 'このファイルはMinIOに保存されています');
|
|
39
|
+
|
|
40
|
+
// JSONデータ
|
|
41
|
+
const userData = { name: 'ずんだもん', age: 3, hobby: 'ずんだ餅作り' };
|
|
42
|
+
await uploadFile('/data/user.json', JSON.stringify(userData, null, 2));
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### ファイルダウンロード
|
|
46
|
+
```javascript
|
|
47
|
+
async function downloadFile(filePath) {
|
|
48
|
+
try {
|
|
49
|
+
if (await fs.exists(filePath)) {
|
|
50
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
51
|
+
console.log(`ファイル内容: ${content}`);
|
|
52
|
+
return content;
|
|
53
|
+
} else {
|
|
54
|
+
console.log('ファイルが見つかりません');
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error('ダウンロードエラー:', error.message);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
await downloadFile('/documents/readme.txt');
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 3. 画像ストレージシステム
|
|
65
|
+
|
|
66
|
+
### 画像アップロード
|
|
67
|
+
```javascript
|
|
68
|
+
import fs from 'fs';
|
|
69
|
+
|
|
70
|
+
async function uploadImage(imagePath, localPath) {
|
|
71
|
+
try {
|
|
72
|
+
// ローカルファイルを読み込み
|
|
73
|
+
const imageBuffer = fs.readFileSync(localPath);
|
|
74
|
+
|
|
75
|
+
// MinIOにアップロード
|
|
76
|
+
await fs.writeFile(imagePath, imageBuffer);
|
|
77
|
+
|
|
78
|
+
console.log(`画像 ${imagePath} をアップロードしました`);
|
|
79
|
+
|
|
80
|
+
// 画像情報を取得
|
|
81
|
+
const stats = await fs.stat(imagePath);
|
|
82
|
+
console.log(`ファイルサイズ: ${stats.size} bytes`);
|
|
83
|
+
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error('画像アップロードエラー:', error.message);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await uploadImage('/images/profile/avatar.jpg', './local-avatar.jpg');
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### サムネイル管理
|
|
93
|
+
```javascript
|
|
94
|
+
async function createThumbnailStructure(userId) {
|
|
95
|
+
const userDir = `/images/users/${userId}`;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
// ユーザー画像ディレクトリ作成
|
|
99
|
+
await fs.mkdir(userDir, { recursive: true });
|
|
100
|
+
await fs.mkdir(`${userDir}/thumbnails`, { recursive: true });
|
|
101
|
+
await fs.mkdir(`${userDir}/originals`, { recursive: true });
|
|
102
|
+
|
|
103
|
+
console.log(`ユーザー ${userId} の画像フォルダを作成しました`);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error('フォルダ作成エラー:', error.message);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await createThumbnailStructure('user123');
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 4. ログ管理システム
|
|
113
|
+
|
|
114
|
+
### ログ書き込み
|
|
115
|
+
```javascript
|
|
116
|
+
async function writeLog(level, message) {
|
|
117
|
+
const timestamp = new Date().toISOString();
|
|
118
|
+
const logEntry = `[${timestamp}] ${level.toUpperCase()}: ${message}\n`;
|
|
119
|
+
|
|
120
|
+
const today = new Date().toISOString().split('T')[0];
|
|
121
|
+
const logPath = `/logs/${today}.log`;
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
// 既存ログがあるかチェック
|
|
125
|
+
let existingLog = '';
|
|
126
|
+
if (await fs.exists(logPath)) {
|
|
127
|
+
existingLog = await fs.readFile(logPath, 'utf8');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ログエントリを追加
|
|
131
|
+
await fs.writeFile(logPath, existingLog + logEntry);
|
|
132
|
+
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('ログ書き込みエラー:', error.message);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
await writeLog('info', 'アプリケーションが開始されました');
|
|
139
|
+
await writeLog('error', 'データベース接続に失敗しました');
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### ログ読み取り
|
|
143
|
+
```javascript
|
|
144
|
+
async function readLogs(date) {
|
|
145
|
+
const logPath = `/logs/${date}.log`;
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
if (await fs.exists(logPath)) {
|
|
149
|
+
const logContent = await fs.readFile(logPath, 'utf8');
|
|
150
|
+
const lines = logContent.split('\n').filter(line => line.trim());
|
|
151
|
+
|
|
152
|
+
console.log(`${date}のログ (${lines.length}エントリ):`);
|
|
153
|
+
lines.forEach(line => console.log(line));
|
|
154
|
+
} else {
|
|
155
|
+
console.log(`${date}のログファイルは存在しません`);
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error('ログ読み取りエラー:', error.message);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await readLogs('2025-06-04');
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## 5. バックアップシステム
|
|
166
|
+
|
|
167
|
+
### データバックアップ
|
|
168
|
+
```javascript
|
|
169
|
+
async function backupData(sourceDir, backupDir) {
|
|
170
|
+
try {
|
|
171
|
+
// バックアップディレクトリ作成
|
|
172
|
+
await fs.mkdir(backupDir, { recursive: true });
|
|
173
|
+
|
|
174
|
+
// ソースディレクトリのファイル一覧取得
|
|
175
|
+
const files = await fs.readdir(sourceDir);
|
|
176
|
+
|
|
177
|
+
console.log(`${files.length}ファイルをバックアップします...`);
|
|
178
|
+
|
|
179
|
+
for (const file of files) {
|
|
180
|
+
const sourcePath = `${sourceDir}/${file}`;
|
|
181
|
+
const backupPath = `${backupDir}/${file}`;
|
|
182
|
+
|
|
183
|
+
// ファイルコピー
|
|
184
|
+
await fs.copyFile(sourcePath, backupPath);
|
|
185
|
+
console.log(`✓ ${file} をバックアップしました`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.log('バックアップ完了');
|
|
189
|
+
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error('バックアップエラー:', error.message);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
196
|
+
await backupData('/important-data', `/backups/${timestamp}`);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## 6. ストリーミング処理
|
|
200
|
+
|
|
201
|
+
### 大きなファイルのストリーミング
|
|
202
|
+
```javascript
|
|
203
|
+
async function streamLargeFile(filePath) {
|
|
204
|
+
try {
|
|
205
|
+
const readStream = await fs.createReadStream(filePath);
|
|
206
|
+
|
|
207
|
+
let totalBytes = 0;
|
|
208
|
+
|
|
209
|
+
readStream.on('data', (chunk) => {
|
|
210
|
+
totalBytes += chunk.length;
|
|
211
|
+
console.log(`受信: ${chunk.length} bytes (合計: ${totalBytes})`);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
readStream.on('end', () => {
|
|
215
|
+
console.log(`ストリーミング完了: 合計 ${totalBytes} bytes`);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
readStream.on('error', (error) => {
|
|
219
|
+
console.error('ストリーミングエラー:', error.message);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error('ストリーム作成エラー:', error.message);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
await streamLargeFile('/videos/large-movie.mp4');
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### ストリーミングアップロード
|
|
231
|
+
```javascript
|
|
232
|
+
function streamUpload(filePath, dataGenerator) {
|
|
233
|
+
return new Promise((resolve, reject) => {
|
|
234
|
+
const writeStream = fs.createWriteStream(filePath);
|
|
235
|
+
|
|
236
|
+
writeStream.on('finish', () => {
|
|
237
|
+
console.log('アップロード完了');
|
|
238
|
+
resolve();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
writeStream.on('error', reject);
|
|
242
|
+
|
|
243
|
+
// データを段階的に書き込み
|
|
244
|
+
for (const data of dataGenerator()) {
|
|
245
|
+
writeStream.write(data);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
writeStream.end();
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// 大きなCSVファイルの生成例
|
|
253
|
+
function* generateCsvData() {
|
|
254
|
+
yield 'id,name,email\n';
|
|
255
|
+
for (let i = 1; i <= 10000; i++) {
|
|
256
|
+
yield `${i},User${i},user${i}@example.com\n`;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
await streamUpload('/data/users.csv', generateCsvData);
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## 7. ファイル管理ユーティリティ
|
|
264
|
+
|
|
265
|
+
### ディレクトリサイズ計算
|
|
266
|
+
```javascript
|
|
267
|
+
async function calculateDirectorySize(dirPath) {
|
|
268
|
+
try {
|
|
269
|
+
const files = await fs.readdir(dirPath);
|
|
270
|
+
let totalSize = 0;
|
|
271
|
+
|
|
272
|
+
for (const file of files) {
|
|
273
|
+
const filePath = `${dirPath}/${file}`;
|
|
274
|
+
const stats = await fs.stat(filePath);
|
|
275
|
+
totalSize += stats.size;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
console.log(`${dirPath} のサイズ: ${totalSize} bytes`);
|
|
279
|
+
return totalSize;
|
|
280
|
+
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.error('サイズ計算エラー:', error.message);
|
|
283
|
+
return 0;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
await calculateDirectorySize('/documents');
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### ファイルクリーンアップ
|
|
291
|
+
```javascript
|
|
292
|
+
async function cleanupOldFiles(dirPath, daysOld = 30) {
|
|
293
|
+
try {
|
|
294
|
+
const files = await fs.readdir(dirPath);
|
|
295
|
+
const cutoffDate = new Date();
|
|
296
|
+
cutoffDate.setDate(cutoffDate.getDate() - daysOld);
|
|
297
|
+
|
|
298
|
+
let deletedCount = 0;
|
|
299
|
+
|
|
300
|
+
for (const file of files) {
|
|
301
|
+
const filePath = `${dirPath}/${file}`;
|
|
302
|
+
const stats = await fs.stat(filePath);
|
|
303
|
+
|
|
304
|
+
if (stats.mtime < cutoffDate) {
|
|
305
|
+
await fs.unlink(filePath);
|
|
306
|
+
console.log(`削除: ${file}`);
|
|
307
|
+
deletedCount++;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
console.log(`${deletedCount}ファイルを削除しました`);
|
|
312
|
+
|
|
313
|
+
} catch (error) {
|
|
314
|
+
console.error('クリーンアップエラー:', error.message);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
await cleanupOldFiles('/temp', 7); // 7日以上古いファイルを削除
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## 8. エラーハンドリングのベストプラクティス
|
|
322
|
+
|
|
323
|
+
```javascript
|
|
324
|
+
async function robustFileOperation(filePath, data) {
|
|
325
|
+
const maxRetries = 3;
|
|
326
|
+
let retries = 0;
|
|
327
|
+
|
|
328
|
+
while (retries < maxRetries) {
|
|
329
|
+
try {
|
|
330
|
+
await fs.writeFile(filePath, data);
|
|
331
|
+
console.log('ファイル操作成功');
|
|
332
|
+
return;
|
|
333
|
+
|
|
334
|
+
} catch (error) {
|
|
335
|
+
retries++;
|
|
336
|
+
|
|
337
|
+
if (error.code === 'ENOENT') {
|
|
338
|
+
// ディレクトリが存在しない場合は作成
|
|
339
|
+
const dir = filePath.substring(0, filePath.lastIndexOf('/'));
|
|
340
|
+
await fs.mkdir(dir, { recursive: true });
|
|
341
|
+
|
|
342
|
+
} else if (error.code === 'EACCES') {
|
|
343
|
+
console.error('アクセス権限エラー:', error.message);
|
|
344
|
+
break;
|
|
345
|
+
|
|
346
|
+
} else if (retries < maxRetries) {
|
|
347
|
+
console.log(`リトライ ${retries}/${maxRetries}: ${error.message}`);
|
|
348
|
+
await new Promise(resolve => setTimeout(resolve, 1000 * retries));
|
|
349
|
+
|
|
350
|
+
} else {
|
|
351
|
+
console.error('最大リトライ数に達しました:', error.message);
|
|
352
|
+
throw error;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
await robustFileOperation('/new-dir/important-file.txt', 'Important data');
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## 9. パフォーマンス最適化
|
|
362
|
+
|
|
363
|
+
### 並列処理
|
|
364
|
+
```javascript
|
|
365
|
+
async function parallelUpload(files) {
|
|
366
|
+
const uploadPromises = files.map(async ({ path, content }) => {
|
|
367
|
+
try {
|
|
368
|
+
await fs.writeFile(path, content);
|
|
369
|
+
return { path, success: true };
|
|
370
|
+
} catch (error) {
|
|
371
|
+
return { path, success: false, error: error.message };
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const results = await Promise.all(uploadPromises);
|
|
376
|
+
|
|
377
|
+
const successful = results.filter(r => r.success).length;
|
|
378
|
+
const failed = results.filter(r => !r.success).length;
|
|
379
|
+
|
|
380
|
+
console.log(`アップロード完了: 成功 ${successful}, 失敗 ${failed}`);
|
|
381
|
+
|
|
382
|
+
return results;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const filesToUpload = [
|
|
386
|
+
{ path: '/batch/file1.txt', content: 'Content 1' },
|
|
387
|
+
{ path: '/batch/file2.txt', content: 'Content 2' },
|
|
388
|
+
{ path: '/batch/file3.txt', content: 'Content 3' }
|
|
389
|
+
];
|
|
390
|
+
|
|
391
|
+
await parallelUpload(filesToUpload);
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
これらの例を参考に、MinIOをファイルシステムとして活用したアプリケーションを開発できます。`fs-minio`ライブラリにより、従来のファイルシステム操作をクラウドストレージ環境でも同じように実行できるのだ!
|
package/package.json
CHANGED
package/quick-test.js
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
// quick-test.js - Quick test of fs-
|
|
1
|
+
// quick-test.js - Quick test of fs-object-storage library
|
|
2
2
|
|
|
3
|
-
console.log('Starting fs-
|
|
3
|
+
console.log('Starting fs-object-storage quick test...');
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { ObjectStorage } from './src/index.js';
|
|
6
6
|
|
|
7
7
|
async function quickTest() {
|
|
8
8
|
try {
|
|
9
|
-
console.log('Creating client...');
|
|
9
|
+
console.log('Creating client...');
|
|
10
|
+
const fs = new ObjectStorage({
|
|
10
11
|
endpoint: 'localhost:9000',
|
|
11
12
|
accessKey: 'minioadmin',
|
|
12
13
|
secretKey: 'minioadmin123',
|
|
13
|
-
|
|
14
|
+
bucket: 'quick-test',
|
|
14
15
|
useSSL: false
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
console.log('Initializing client...');
|
|
18
|
-
await
|
|
19
|
+
await fs.initialize();
|
|
19
20
|
console.log('Client initialized!');
|
|
20
21
|
|
|
21
22
|
console.log('Writing test file...');
|
|
22
|
-
await
|
|
23
|
+
await fs.writeFile('/test.txt', 'Hello World!', 'utf8');
|
|
23
24
|
console.log('File written!');
|
|
24
25
|
|
|
25
26
|
console.log('Reading test file...');
|
|
26
|
-
const content = await
|
|
27
|
+
const content = await fs.readFile('/test.txt', 'utf8');
|
|
27
28
|
console.log('File content:', content);
|
|
28
29
|
|
|
29
30
|
console.log('Test completed successfully!');
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
// fs-
|
|
1
|
+
// fs-object-storage-test.js - Test the fs-object-storage library implementation
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import path from 'path';
|
|
3
|
+
import { ObjectStorage } from '../src/index.js';
|
|
5
4
|
|
|
6
|
-
async function
|
|
7
|
-
console.log('🧪 Testing fs-
|
|
5
|
+
async function testObjectStorageLibrary() {
|
|
6
|
+
console.log('🧪 Testing fs-object-storage library implementation...\n');
|
|
8
7
|
|
|
9
8
|
// Create client instance
|
|
10
|
-
const
|
|
9
|
+
const fs = new ObjectStorage({
|
|
11
10
|
endpoint: 'localhost:9000',
|
|
12
11
|
accessKey: 'minioadmin',
|
|
13
12
|
secretKey: 'minioadmin123',
|
|
@@ -18,23 +17,23 @@ async function testFsMinioLibrary() {
|
|
|
18
17
|
|
|
19
18
|
try {
|
|
20
19
|
console.log('📝 Initializing client...');
|
|
21
|
-
await
|
|
20
|
+
await fs.initialize();
|
|
22
21
|
console.log('✅ Client initialized successfully\n');
|
|
23
22
|
|
|
24
23
|
// Test 1: Write file
|
|
25
24
|
console.log('📝 Test 1: writeFile()');
|
|
26
25
|
const testContent = 'Hello from fs-minio library!\nThis is a test file.\n今日は良い天気ですね。';
|
|
27
|
-
await
|
|
26
|
+
await fs.writeFile('/data/hello.txt', testContent, 'utf8');
|
|
28
27
|
console.log('✅ File written successfully\n');
|
|
29
28
|
|
|
30
29
|
// Test 2: Check if file exists
|
|
31
30
|
console.log('📝 Test 2: exists()');
|
|
32
|
-
const fileExists = await
|
|
31
|
+
const fileExists = await fs.exists('/data/hello.txt');
|
|
33
32
|
console.log(`✅ File exists: ${fileExists}\n`);
|
|
34
33
|
|
|
35
34
|
// Test 3: Read file
|
|
36
35
|
console.log('📝 Test 3: readFile()');
|
|
37
|
-
const readContent = await
|
|
36
|
+
const readContent = await fs.readFile('/data/hello.txt', 'utf8');
|
|
38
37
|
console.log(`✅ File content: "${readContent}"\n`);
|
|
39
38
|
|
|
40
39
|
// Verify content matches
|
|
@@ -46,7 +45,7 @@ async function testFsMinioLibrary() {
|
|
|
46
45
|
|
|
47
46
|
// Test 4: Get file stats
|
|
48
47
|
console.log('📝 Test 4: stat()');
|
|
49
|
-
const stats = await
|
|
48
|
+
const stats = await fs.stat('/data/hello.txt');
|
|
50
49
|
console.log('✅ File stats:');
|
|
51
50
|
console.log(` Size: ${stats.size} bytes`);
|
|
52
51
|
console.log(` Modified: ${stats.mtime}`);
|
|
@@ -56,29 +55,29 @@ async function testFsMinioLibrary() {
|
|
|
56
55
|
// Test 5: Write binary file
|
|
57
56
|
console.log('📝 Test 5: writeFile() with binary data');
|
|
58
57
|
const binaryData = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]); // PNG header
|
|
59
|
-
await
|
|
58
|
+
await fs.writeFile('/images/test.png', binaryData);
|
|
60
59
|
console.log('✅ Binary file written successfully\n');
|
|
61
60
|
|
|
62
61
|
// Test 6: Read binary file
|
|
63
62
|
console.log('📝 Test 6: readFile() binary data');
|
|
64
|
-
const readBinary = await
|
|
63
|
+
const readBinary = await fs.readFile('/images/test.png');
|
|
65
64
|
console.log(`✅ Binary data read: ${readBinary.length} bytes`);
|
|
66
65
|
console.log(` First 8 bytes: ${Array.from(readBinary.slice(0, 8)).map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}\n`);
|
|
67
66
|
|
|
68
67
|
// Test 7: Create directory
|
|
69
68
|
console.log('📝 Test 7: mkdir()');
|
|
70
|
-
await
|
|
69
|
+
await fs.mkdir('/documents/projects', { recursive: true });
|
|
71
70
|
console.log('✅ Directory created successfully\n');
|
|
72
71
|
|
|
73
72
|
// Test 8: List files in root
|
|
74
73
|
console.log('📝 Test 8: readdir()');
|
|
75
|
-
const rootFiles = await
|
|
74
|
+
const rootFiles = await fs.readdir('/');
|
|
76
75
|
console.log(`✅ Root directory contents: ${JSON.stringify(rootFiles)}\n`);
|
|
77
76
|
|
|
78
77
|
// Test 9: Test error handling (non-existent file)
|
|
79
78
|
console.log('📝 Test 9: Error handling');
|
|
80
79
|
try {
|
|
81
|
-
await
|
|
80
|
+
await fs.readFile('/nonexistent/file.txt');
|
|
82
81
|
console.log('❌ Should have thrown error');
|
|
83
82
|
} catch (error) {
|
|
84
83
|
console.log(`✅ Correctly threw error: ${error.code} - ${error.message}\n`);
|
|
@@ -86,26 +85,23 @@ async function testFsMinioLibrary() {
|
|
|
86
85
|
|
|
87
86
|
// Test 10: Delete file
|
|
88
87
|
console.log('📝 Test 10: unlink()');
|
|
89
|
-
await
|
|
88
|
+
await fs.unlink('/data/hello.txt');
|
|
90
89
|
console.log('✅ File deleted successfully\n');
|
|
91
90
|
|
|
92
91
|
// Test 11: Verify file is deleted
|
|
93
92
|
console.log('📝 Test 11: Verify deletion');
|
|
94
|
-
const fileExistsAfterDelete = await
|
|
93
|
+
const fileExistsAfterDelete = await fs.exists('/data/hello.txt');
|
|
95
94
|
console.log(`✅ File exists after delete: ${fileExistsAfterDelete}\n`);
|
|
96
95
|
|
|
97
96
|
// Test 12: Test streams
|
|
98
97
|
console.log('📝 Test 12: createReadStream()');
|
|
99
98
|
const streamContent = 'This is stream test content\nLine 2\nLine 3';
|
|
100
|
-
await
|
|
101
|
-
|
|
102
|
-
const readStream = await client.createReadStream('/streams/test.txt');
|
|
99
|
+
await fs.writeFile('/streams/test.txt', streamContent);
|
|
100
|
+
const readStream = await fs.createReadStream('/streams/test.txt');
|
|
103
101
|
const chunks = [];
|
|
104
|
-
|
|
105
102
|
readStream.on('data', (chunk) => {
|
|
106
103
|
chunks.push(chunk);
|
|
107
104
|
});
|
|
108
|
-
|
|
109
105
|
await new Promise((resolve, reject) => {
|
|
110
106
|
readStream.on('end', () => {
|
|
111
107
|
const streamData = Buffer.concat(chunks).toString('utf8');
|
|
@@ -132,4 +128,4 @@ async function testFsMinioLibrary() {
|
|
|
132
128
|
}
|
|
133
129
|
|
|
134
130
|
// Run tests
|
|
135
|
-
|
|
131
|
+
testObjectStorageLibrary().catch(console.error);
|
package/src/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Readable, Writable } from 'stream';
|
|
2
2
|
|
|
3
|
-
export interface
|
|
3
|
+
export interface Config {
|
|
4
4
|
endPoint: string;
|
|
5
5
|
port?: number;
|
|
6
6
|
useSSL?: boolean;
|
|
@@ -41,8 +41,8 @@ export interface FileSystemError extends Error {
|
|
|
41
41
|
syscall?: string;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export default class
|
|
45
|
-
constructor(config:
|
|
44
|
+
export default class ObjectStorage {
|
|
45
|
+
constructor(config: Config);
|
|
46
46
|
|
|
47
47
|
// File operations
|
|
48
48
|
readFile(path: string): Promise<Buffer>;
|
|
@@ -95,4 +95,4 @@ export class StreamConverter {
|
|
|
95
95
|
static normalizeData(data: any): Buffer;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
export default
|
|
98
|
+
export default ObjectStorage;
|
package/src/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
// fs-
|
|
1
|
+
// fs-object-storage - fs-compatible API for MinIO/S3 object storage
|
|
2
2
|
// Main entry point
|
|
3
3
|
|
|
4
|
-
import
|
|
4
|
+
import ObjectStorage from './lib/ObjectStorage.js';
|
|
5
5
|
import PathConverter from './lib/PathConverter.js';
|
|
6
6
|
import StreamConverter from './lib/StreamConverter.js';
|
|
7
7
|
import ErrorHandler from './lib/ErrorHandler.js';
|
|
8
8
|
|
|
9
9
|
export {
|
|
10
|
-
|
|
10
|
+
ObjectStorage,
|
|
11
11
|
PathConverter,
|
|
12
12
|
StreamConverter,
|
|
13
13
|
ErrorHandler
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
// Default export for convenience
|
|
17
|
-
export default
|
|
17
|
+
export default ObjectStorage;
|
package/src/lib/ErrorHandler.js
CHANGED
|
@@ -91,7 +91,7 @@ class ErrorHandler {
|
|
|
91
91
|
* @param {string} operation - Operation name
|
|
92
92
|
* @returns {Error} fs-compatible error
|
|
93
93
|
*/
|
|
94
|
-
static
|
|
94
|
+
static createFileSystemError(code, path = null, operation = 'open') {
|
|
95
95
|
// ENOENT, EACCES, EEXIST, EINVAL, ENAMETOOLONG など標準エラーコードにも対応
|
|
96
96
|
let errorInfo = this.errorMapping[code];
|
|
97
97
|
if (!errorInfo) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
//
|
|
1
|
+
// ObjectStorage.js - Main fs-compatible client for MinIO/S3 operations
|
|
2
2
|
|
|
3
3
|
import { Client as MinioClient } from 'minio';
|
|
4
4
|
import PathConverter from './PathConverter.js';
|
|
5
5
|
import StreamConverter from './StreamConverter.js';
|
|
6
6
|
import ErrorHandler from './ErrorHandler.js';
|
|
7
7
|
|
|
8
|
-
class
|
|
8
|
+
class ObjectStorage {
|
|
9
9
|
/**
|
|
10
|
-
* Create
|
|
10
|
+
* Create ObjectStorage instance
|
|
11
11
|
* @param {Object} options - Configuration options
|
|
12
12
|
* @param {string} options.endpoint - MinIO endpoint (e.g., 'localhost:9000')
|
|
13
13
|
* @param {string} options.accessKey - Access key
|
|
@@ -477,4 +477,4 @@ class FsMinioClient {
|
|
|
477
477
|
}
|
|
478
478
|
}
|
|
479
479
|
|
|
480
|
-
export default
|
|
480
|
+
export default ObjectStorage;
|