web-dc-api 0.1.4 → 0.1.6

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.
Files changed (148) hide show
  1. package/dist/cjs/index.js +1 -1
  2. package/dist/dc.min.js +1 -1
  3. package/dist/esm/index.js +1 -1
  4. package/dist/index.d.ts +934 -878
  5. package/package.json +4 -8
  6. package/dist/cjs/helia-core-B1Xqha7a.js +0 -1
  7. package/dist/cjs/helia-core-D8Uv1KjQ.js +0 -1
  8. package/dist/cjs/polkadot-api-7PhQf3ws.js +0 -1
  9. package/dist/cjs/polkadot-api-CtrJVWuZ.js +0 -1
  10. package/dist/esm/chunks/helia-core-BxMqyK2Y.js +0 -1
  11. package/dist/esm/chunks/helia-core-DMXRpcO-.js +0 -1
  12. package/dist/esm/chunks/polkadot-api-5Y9Bw8VT.js +0 -1
  13. package/dist/esm/chunks/polkadot-api-D69Ioun_.js +0 -1
  14. package/lib/common/blowfish/block.ts +0 -259
  15. package/lib/common/blowfish/cipher.ts +0 -144
  16. package/lib/common/blowfish/const.ts +0 -195
  17. package/lib/common/chain.ts +0 -469
  18. package/lib/common/commonclient.ts +0 -202
  19. package/lib/common/constants.ts +0 -55
  20. package/lib/common/dc-key/ed25519.ts +0 -343
  21. package/lib/common/dc-key/keyManager.ts +0 -424
  22. package/lib/common/dcapi.ts +0 -98
  23. package/lib/common/dcutil.ts +0 -627
  24. package/lib/common/define.ts +0 -70
  25. package/lib/common/error.ts +0 -67
  26. package/lib/common/grpc-dc.ts +0 -104
  27. package/lib/common/module-system.ts +0 -184
  28. package/lib/common/service-worker.ts +0 -234
  29. package/lib/common/types/types.ts +0 -344
  30. package/lib/dc.ts +0 -701
  31. package/lib/implements/account/client.ts +0 -185
  32. package/lib/implements/account/manager.ts +0 -683
  33. package/lib/implements/aiproxy/client.ts +0 -357
  34. package/lib/implements/aiproxy/manager.ts +0 -670
  35. package/lib/implements/cache/client.ts +0 -105
  36. package/lib/implements/cache/manager.ts +0 -127
  37. package/lib/implements/comment/client.ts +0 -982
  38. package/lib/implements/comment/manager.ts +0 -1151
  39. package/lib/implements/dc/client.ts +0 -51
  40. package/lib/implements/dc/manager.ts +0 -33
  41. package/lib/implements/file/client.ts +0 -253
  42. package/lib/implements/file/file-cache-manager.ts +0 -142
  43. package/lib/implements/file/manager.ts +0 -1240
  44. package/lib/implements/file/seekableFileStream.ts +0 -344
  45. package/lib/implements/file/streamwriter.ts +0 -322
  46. package/lib/implements/keyvalue/client.ts +0 -376
  47. package/lib/implements/keyvalue/manager.ts +0 -759
  48. package/lib/implements/message/client.ts +0 -250
  49. package/lib/implements/message/manager.ts +0 -215
  50. package/lib/implements/threaddb/cbor/coding.ts +0 -62
  51. package/lib/implements/threaddb/cbor/event.ts +0 -336
  52. package/lib/implements/threaddb/cbor/node.ts +0 -542
  53. package/lib/implements/threaddb/cbor/record.ts +0 -398
  54. package/lib/implements/threaddb/common/AsyncMutex.ts +0 -24
  55. package/lib/implements/threaddb/common/addrinfo.ts +0 -135
  56. package/lib/implements/threaddb/common/dispatcher.ts +0 -81
  57. package/lib/implements/threaddb/common/idbstore-adapter.ts +0 -260
  58. package/lib/implements/threaddb/common/json-patcher.ts +0 -204
  59. package/lib/implements/threaddb/common/key.ts +0 -290
  60. package/lib/implements/threaddb/common/level-adapter.ts +0 -235
  61. package/lib/implements/threaddb/common/lineReader.ts +0 -79
  62. package/lib/implements/threaddb/common/logstore.ts +0 -215
  63. package/lib/implements/threaddb/common/transformed-datastore.ts +0 -308
  64. package/lib/implements/threaddb/core/app.ts +0 -206
  65. package/lib/implements/threaddb/core/core.ts +0 -230
  66. package/lib/implements/threaddb/core/db.ts +0 -249
  67. package/lib/implements/threaddb/core/event.ts +0 -54
  68. package/lib/implements/threaddb/core/head.ts +0 -89
  69. package/lib/implements/threaddb/core/identity.ts +0 -171
  70. package/lib/implements/threaddb/core/logstore.ts +0 -137
  71. package/lib/implements/threaddb/core/options.ts +0 -14
  72. package/lib/implements/threaddb/core/record.ts +0 -54
  73. package/lib/implements/threaddb/db/collection.ts +0 -1910
  74. package/lib/implements/threaddb/db/db.ts +0 -698
  75. package/lib/implements/threaddb/db/json2Query.ts +0 -192
  76. package/lib/implements/threaddb/db/query.ts +0 -524
  77. package/lib/implements/threaddb/dbclient.ts +0 -543
  78. package/lib/implements/threaddb/dbmanager.ts +0 -1906
  79. package/lib/implements/threaddb/lsstoreds/addr_book.ts +0 -549
  80. package/lib/implements/threaddb/lsstoreds/cache.ts +0 -36
  81. package/lib/implements/threaddb/lsstoreds/cyclic_batch.ts +0 -87
  82. package/lib/implements/threaddb/lsstoreds/global.ts +0 -151
  83. package/lib/implements/threaddb/lsstoreds/headbook.ts +0 -373
  84. package/lib/implements/threaddb/lsstoreds/keybook.ts +0 -297
  85. package/lib/implements/threaddb/lsstoreds/logstore.ts +0 -29
  86. package/lib/implements/threaddb/lsstoreds/metadata.ts +0 -223
  87. package/lib/implements/threaddb/net/define.ts +0 -149
  88. package/lib/implements/threaddb/net/grpcClient.ts +0 -589
  89. package/lib/implements/threaddb/net/grpcserver.ts +0 -146
  90. package/lib/implements/threaddb/net/net.ts +0 -2047
  91. package/lib/implements/threaddb/pb/lstore.proto +0 -38
  92. package/lib/implements/threaddb/pb/lstore.ts +0 -393
  93. package/lib/implements/threaddb/pb/lstore_pb.d.ts +0 -433
  94. package/lib/implements/threaddb/pb/lstore_pb.js +0 -1085
  95. package/lib/implements/threaddb/pb/net.proto +0 -194
  96. package/lib/implements/threaddb/pb/net_pb.d.ts +0 -2349
  97. package/lib/implements/threaddb/pb/net_pb.js +0 -5525
  98. package/lib/implements/threaddb/pb/proto-custom-types.ts +0 -212
  99. package/lib/implements/util/client.ts +0 -72
  100. package/lib/implements/util/manager.ts +0 -146
  101. package/lib/implements/wallet/manager.ts +0 -671
  102. package/lib/index.ts +0 -57
  103. package/lib/interfaces/DCContext.ts +0 -51
  104. package/lib/interfaces/aiproxy-interface.ts +0 -145
  105. package/lib/interfaces/auth-interface.ts +0 -118
  106. package/lib/interfaces/cache-interface.ts +0 -22
  107. package/lib/interfaces/client-interface.ts +0 -11
  108. package/lib/interfaces/comment-interface.ts +0 -167
  109. package/lib/interfaces/components/news-component.ts +0 -0
  110. package/lib/interfaces/database-interface.ts +0 -169
  111. package/lib/interfaces/file-interface.ts +0 -120
  112. package/lib/interfaces/index.ts +0 -10
  113. package/lib/interfaces/keyvalue-interface.ts +0 -156
  114. package/lib/interfaces/message-interface.ts +0 -22
  115. package/lib/interfaces/util-interface.ts +0 -31
  116. package/lib/modules/aiproxy-module.ts +0 -246
  117. package/lib/modules/auth-module.ts +0 -753
  118. package/lib/modules/cache-module.ts +0 -99
  119. package/lib/modules/client-module.ts +0 -71
  120. package/lib/modules/comment-module.ts +0 -429
  121. package/lib/modules/components/news-components.ts +0 -390
  122. package/lib/modules/database-module.ts +0 -598
  123. package/lib/modules/file-module.ts +0 -291
  124. package/lib/modules/index.ts +0 -13
  125. package/lib/modules/keyvalue-module.ts +0 -379
  126. package/lib/modules/message-module.ts +0 -107
  127. package/lib/modules/util-module.ts +0 -148
  128. package/lib/polyfills/process-env-browser.ts +0 -1
  129. package/lib/proto/datasource.ts +0 -93
  130. package/lib/proto/dcnet.proto +0 -1601
  131. package/lib/proto/dcnet_proto.d.ts +0 -22857
  132. package/lib/proto/dcnet_proto.js +0 -55204
  133. package/lib/proto/dcnet_proto_sparse.js +0 -55166
  134. package/lib/proto/oidfetch.proto +0 -25
  135. package/lib/proto/oidfetch_proto.d.ts +0 -585
  136. package/lib/proto/oidfetch_proto.js +0 -1247
  137. package/lib/serverless/babel-browser.ts +0 -39
  138. package/lib/serverless/base_entity.ts +0 -78
  139. package/lib/serverless/base_repository.ts +0 -414
  140. package/lib/serverless/browser_schema_extractor.ts +0 -283
  141. package/lib/serverless/decorator_factory.ts +0 -322
  142. package/lib/util/BrowserLineReader.ts +0 -73
  143. package/lib/util/base64.ts +0 -105
  144. package/lib/util/bcrypt.ts +0 -206
  145. package/lib/util/curve25519Encryption.ts +0 -418
  146. package/lib/util/dccrypt.ts +0 -73
  147. package/lib/util/logger.ts +0 -104
  148. package/lib/util/utils.ts +0 -289
@@ -1,1240 +0,0 @@
1
- import { UploadStatus, type DCConnectInfo } from "../../common/types/types";
2
- import { FileClient } from "./client";
3
- import type { HeliaLibp2p } from "helia";
4
- import { ChainUtil } from "../../common/chain";
5
- import { Errors as GErrors } from "../../common/error";
6
-
7
- import {
8
- compareByteArrays,
9
- mergeUInt8Arrays,
10
- sleep,
11
- uint32ToLittleEndianBytes,
12
- uint64ToBigEndianBytes,
13
- uint64ToLittleEndianBytes,
14
- } from "../../util/utils";
15
-
16
- import { unixfs } from "@helia/unixfs";
17
- import { SymmetricKey } from "../threaddb/common/key";
18
- import { CID, Version } from "multiformats/cid";
19
- import { BrowserType, DcUtil } from "../../common/dcutil";
20
- import toBuffer from "it-to-buffer";
21
- import { decryptContent } from "../../util/dccrypt";
22
- import * as buffer from "buffer/";
23
- import { Uint8ArrayList } from "uint8arraylist";
24
- import { Libp2p, Stream } from "@libp2p/interface";
25
- import { cidNeedConnect } from "../../common/constants";
26
- import { SeekableFileStream } from "./seekableFileStream";
27
- import { AccountClient } from "../account/client";
28
- import { DCContext } from "../../../lib/interfaces/DCContext";
29
- const { Buffer } = buffer;
30
-
31
- const NonceBytes = 12;
32
- const TagBytes = 16;
33
- const dcFileHead = "$$dcfile$$";
34
- const chunkSize = 3 * 1024 * 1024; // 3MB chunks
35
- // 创建一个可以取消的信号
36
- const controller = new AbortController();
37
- const { signal } = controller;
38
-
39
- // 错误定义
40
- export class FileError extends Error {
41
- constructor(message: string) {
42
- super(message);
43
- this.name = "FileError";
44
- }
45
- }
46
- export const Errors = {
47
- ErrNoDcPeerConnected: new FileError("no dc peer connected"),
48
- ErrNoFileChose: new FileError("no file choose"),
49
- ErrNoPeerIdIsNull: new FileError("peerId is null"),
50
- ErrNoNeedUpload: new FileError("no need upload"),
51
- ErrPublicKeyIsNull: new FileError("publickey is null"),
52
- };
53
-
54
- export interface MediaController {
55
- restart(): {
56
- videoElement: HTMLVideoElement;
57
- mediaUrl: string;
58
- controller: MediaController;
59
- };
60
- dispose(): void;
61
- }
62
- interface CustomMessage {
63
- type: number; // uint8 (1字节)
64
- version: number; // uint16 (2字节, 大端序)
65
- payload: Uint8Array; // 二进制数据
66
- }
67
-
68
- export class FileManager {
69
- dc: DcUtil;
70
- connectedDc: DCConnectInfo = {};
71
- chainUtil: ChainUtil;
72
- dcNodeClient: HeliaLibp2p<Libp2p>;
73
- context: DCContext;
74
- constructor(
75
- dc: DcUtil,
76
- connectedDc: DCConnectInfo,
77
- chainUtil: ChainUtil,
78
- dcNodeClient: HeliaLibp2p<Libp2p>,
79
- context: DCContext
80
- ) {
81
- this.dc = dc;
82
- this.connectedDc = connectedDc;
83
- this.chainUtil = chainUtil;
84
- this.dcNodeClient = dcNodeClient;
85
- this.context = context;
86
- }
87
- // 处理文件头
88
- async _processHeader(
89
- pubkeyBytes: Uint8Array,
90
- fileSize: number,
91
- content: Uint8Array,
92
- isFirstChunk: boolean
93
- ): Promise<Uint8Array> {
94
- if (isFirstChunk) {
95
- // 计算 pubkey 的 hash
96
- const pubkeyHash = await crypto.subtle.digest("SHA-256", pubkeyBytes as any);
97
- const pubkeyHashArray = new Uint8Array(pubkeyHash);
98
-
99
- // 创建文件头
100
- const headArray = new TextEncoder().encode(dcFileHead);
101
- const pubkeyHashPart = pubkeyHashArray.slice(10, 24);
102
-
103
- // 创建表示文件大小的字节数组
104
- const realSizeBuffer = uint64ToBigEndianBytes(fileSize);
105
- const realSizeBytes = new Uint8Array(realSizeBuffer);
106
-
107
- // 组合所有部分
108
- const result = new Uint8Array([
109
- ...headArray,
110
- ...pubkeyHashPart,
111
- ...realSizeBytes,
112
- ...content,
113
- ]);
114
-
115
- return result;
116
- }
117
- return content;
118
- }
119
- async _uploadLargeFileAdvanced(
120
- file: File,
121
- resumeState = { offset: 0, chunkHashes: [] },
122
- pubkeyBytes?: Uint8Array,
123
- symKey?: SymmetricKey | null
124
- ): Promise<CID | null> {
125
- const fs = unixfs(this.dcNodeClient);
126
-
127
- let offset = resumeState.offset || 0;
128
- const chunkHashes = resumeState.chunkHashes || {};
129
-
130
- const _this = this;
131
-
132
- // 创建符合流式接口的内容生成器
133
- const contentStream = async function* () {
134
- while (offset < file.size) {
135
- // // 检查取消信号(需要补充signal参数)
136
- // if (_this.signal?.aborted) {
137
- // throw new AbortError('Upload cancelled')
138
- // }
139
-
140
- // 读取分块
141
- const chunk = file.slice(offset, offset + chunkSize);
142
- const arrayBuffer = await chunk.arrayBuffer();
143
- let content = new Uint8Array(arrayBuffer);
144
-
145
- // 加密处理
146
- if (symKey) {
147
- content = await symKey.encrypt(content) as any;
148
- }
149
-
150
- // 文件头处理(仅在第一个分块添加)
151
- if (pubkeyBytes && offset === 0) {
152
- content = await _this._processHeader(
153
- pubkeyBytes,
154
- file.size,
155
- content,
156
- true // isFirstChunk
157
- ) as any;
158
- }
159
-
160
- offset += chunkSize;
161
- yield content;
162
- }
163
- };
164
- const cid = await fs.addByteStream(contentStream(), {
165
- rawLeaves: false,
166
- leafType: "file",
167
- shardSplitThresholdBytes: 256 * 1024,
168
- });
169
- return cid;
170
- }
171
-
172
- /**
173
- * 检查当前连接的节点是否已经绑定到用户账户
174
- * @returns 如果已绑定则返回true,否则返回false
175
- */
176
- async isAccessPeerIdBinded(): Promise<boolean> {
177
- const userInfo = await this.chainUtil.refreshUserInfo(
178
- this.context.getPubkeyRaw()
179
- );
180
-
181
- if (userInfo.requestPeers && userInfo.requestPeers.length > 0) {
182
- for (const peerId of userInfo.requestPeers) {
183
- // 已经是绑定节点,直接返回true
184
- if (peerId === this.connectedDc.nodeAddr?.getPeerId()) {
185
- return true;
186
- }
187
- }
188
- }
189
- return false;
190
- }
191
-
192
- // 上传文件
193
- async addFile(
194
- file: File,
195
- enkey: string,
196
- onUpdateTransmitSize: (status: UploadStatus, size: number) => void,
197
- vaccount?: string
198
- ): Promise<[string | null, Error | null]> {
199
- if (!this.connectedDc?.client) {
200
- return [null, Errors.ErrNoDcPeerConnected];
201
- }
202
- if (!this.connectedDc || !this.connectedDc.nodeAddr) {
203
- console.error("=========Errors.ErrNoDcPeerConnected");
204
- return [null, Errors.ErrNoDcPeerConnected];
205
- }
206
-
207
- // this.dcNodeClient.libp2p.dialProtocol(this.connectedDc.nodeAddr, '/ipfs/bitswap/1.2.0')
208
- const blockHeight = await this.chainUtil.getBlockHeight();
209
- const peerId = this.connectedDc.nodeAddr?.getPeerId();
210
- if (!peerId) {
211
- return [null, Errors.ErrNoPeerIdIsNull];
212
- }
213
- let resCid = "";
214
- try {
215
- if (!(await this.isAccessPeerIdBinded())) {
216
- // 检查当前连接的节点是否已经绑定到用户账户,没绑定,执行绑定
217
- const accountClient = new AccountClient(this.connectedDc.client);
218
- await accountClient.bindAccessPeerToUser(
219
- this.context,
220
- blockHeight ? blockHeight : 0,
221
- peerId
222
- );
223
- //等待绑定信息上链,最多等待20秒
224
- let waitCount = 0;
225
- while (true) {
226
- if (await this.isAccessPeerIdBinded()) {
227
- break;
228
- }
229
- waitCount += 1;
230
- if (waitCount > 30) {
231
- return [null, new FileError("No access auth to peer")]; // 使用正确的错误返回格式
232
- }
233
- await sleep(1000); // 使用毫秒,等同于1秒
234
- }
235
- }
236
- }catch (error: any) {
237
- return [null, error];
238
- }
239
-
240
- try {
241
- const fileSize = file.size;
242
- const symKey =
243
- enkey && enkey.length > 0 ? SymmetricKey.fromString(enkey) : null;
244
- const fs = unixfs(this.dcNodeClient);
245
- const pubkeyBytes = this.context.getPubkeyRaw();
246
- // const peerId = "12D3KooWEGzh4AcbJrfZMfQb63wncBUpscMEEyiMemSWzEnjVCPf";
247
- let nodeAddr = await this.dc?._getNodeAddr(peerId);
248
- if (!nodeAddr) {
249
- return [null, Errors.ErrNoDcPeerConnected];
250
- }
251
- const cid = await this._uploadLargeFileAdvanced(
252
- file,
253
- { offset: 0, chunkHashes: [] },
254
- pubkeyBytes,
255
- symKey
256
- );
257
- if (!cid) {
258
- return [resCid, Errors.ErrNoFileChose];
259
- }
260
- console.log("==========_uploadLargeFileAdvanced", cid.toString());
261
- resCid = cid.toString();
262
- console.log("=========resCid", resCid);
263
-
264
- const stats = await fs.stat(cid);
265
- const filesize = stats.unixfs?.fileSize() || 0;
266
- console.log(
267
- "=========stats",
268
- stats.localFileSize.toString(),
269
- stats.localFileSize.toString(),
270
- stats.fileSize.toString(),
271
- stats.dagSize.toString(),
272
- filesize.toString(),
273
- );
274
- const dagFileSize = Number(stats.localDagSize);
275
- const fileClient = new FileClient(
276
- this.connectedDc.client,
277
- this.dcNodeClient,
278
- this.context
279
- );
280
- let resStatus = 0;
281
- let resFlag = false;
282
- let resError: Error | null = null;
283
- fileClient.storeFile(
284
- dagFileSize,
285
- blockHeight ? blockHeight : 0,
286
- resCid,
287
- peerId,
288
- (status: number, size: number): void => {
289
- resFlag = true;
290
- resStatus = status;
291
- onUpdateTransmitSize(status, size);
292
- },
293
- async (error: Error) => {
294
- resFlag = true;
295
- resError = error;
296
- }
297
- );
298
- //等待storeRes 为true
299
- while (!resFlag) {
300
- await sleep(100);
301
- }
302
- if (resError) {
303
- return [null, resError];
304
- }
305
- if (resStatus === UploadStatus.ERROR || resStatus === UploadStatus.ABNORMAL
306
- || resStatus === UploadStatus.NOSPACE || resStatus === UploadStatus.FILECOUNTERROR
307
- || resStatus === UploadStatus.FILESIZEERROR || resStatus === UploadStatus.PULLERROR) {
308
- //上传失败,不需要操作
309
- return [null, Errors.ErrNoNeedUpload];
310
- }
311
-
312
- //创建文件主动上报流
313
- await this.dc.createTransferStream(
314
- this.dcNodeClient.libp2p,
315
- this.dcNodeClient.blockstore,
316
- nodeAddr,
317
- BrowserType.File,
318
- resCid
319
- );
320
- } catch (error) {
321
- console.error("=========stream close", error);
322
- throw error;
323
- }
324
- return [resCid, null];
325
- }
326
-
327
- /**
328
- * Adds a folder to the DC network using browser FileList
329
- * @param folderInput - Files from a directory input element
330
- * @param enkey - Encryption key
331
- * @param updateTransmitCount - Callback for progress updates
332
- * @param vaccount - Optional virtual account
333
- * @returns Promise with CID string and error if any
334
- */
335
- async addFolder(
336
- fileList: FileList,
337
- enkey: string,
338
- updateTransmitCount: (
339
- status: UploadStatus,
340
- total: number,
341
- progress: number
342
- ) => void,
343
- vaccount?: string
344
- ): Promise<[string | null, Error | null]> {
345
- if (!this.connectedDc?.client) {
346
- return [null, Errors.ErrNoDcPeerConnected];
347
- }
348
- if (!this.connectedDc || !this.connectedDc.nodeAddr) {
349
- console.error("=========Errors.ErrNoDcPeerConnected");
350
- return [null, Errors.ErrNoDcPeerConnected];
351
- }
352
-
353
- // this.dcNodeClient.libp2p.dialProtocol(this.connectedDc.nodeAddr, '/ipfs/bitswap/1.2.0')
354
- const blockHeight = await this.chainUtil.getBlockHeight();
355
- const peerId = this.connectedDc.nodeAddr?.getPeerId();
356
- if (!peerId) {
357
- return [null, Errors.ErrNoPeerIdIsNull];
358
- }
359
- let nodeAddr = await this.dc?._getNodeAddr(peerId);
360
- if (!nodeAddr) {
361
- return [null, Errors.ErrNoDcPeerConnected];
362
- }
363
- try {
364
- if (!(await this.isAccessPeerIdBinded())) {
365
- // 检查当前连接的节点是否已经绑定到用户账户,没绑定,执行绑定
366
- const accountClient = new AccountClient(this.connectedDc.client);
367
- await accountClient.bindAccessPeerToUser(
368
- this.context,
369
- blockHeight ? blockHeight : 0,
370
- peerId
371
- );
372
- //等待绑定信息上链,最多等待20秒
373
- let waitCount = 0;
374
- while (true) {
375
- if (await this.isAccessPeerIdBinded()) {
376
- break;
377
- }
378
- waitCount += 1;
379
- if (waitCount > 30) {
380
- return [null, new FileError("No access auth to peer")]; // 使用正确的错误返回格式
381
- }
382
- await sleep(1000); // 使用毫秒,等同于1秒
383
- }
384
- }
385
- } catch (error: any) {
386
- return [null, error];
387
- }
388
-
389
- try {
390
- // Create IPFS file system interface
391
- const fs = unixfs(this.dcNodeClient);
392
-
393
- // Generate user flag file (dc_ownuser)
394
- let pubkeyBytes = this.context.getPubkeyRaw();
395
-
396
- // Create hash of public key for owner file
397
- const pubkeyHash = await crypto.subtle.digest("SHA-256", pubkeyBytes as any);
398
- const ownerFileContent = new Uint8Array(pubkeyHash);
399
-
400
- // Create folder structure using MFS (memory file system)
401
- // Get root folder name
402
- const rootFolderName = this.extractRootFolderName(fileList);
403
- //排除掉 dc_ownuser 文件,然后又加上去,防止重复添加
404
- const files = Array.from(fileList).filter(
405
- (file) => file.name !== "dc_ownuser"
406
- );
407
- //将ownerFileContent作为文件内容,最后添加到根目录
408
- const ownerPath = rootFolderName + "/dc_ownuser";
409
- files.push(
410
- new File([ownerFileContent], "dc_ownuser", { type: "text/plain" })
411
- );
412
-
413
- // 将文件路径与内容流映射
414
- const source = Array.from(files).map((file) => ({
415
- path: file.name === "dc_ownuser" ? ownerPath : file.webkitRelativePath, // 获取文件相对路径
416
- content: this.fileToStream(file, enkey), // 使用文件流
417
- }));
418
-
419
- const results = fs.addAll(source);
420
- let rootCID: CID<unknown, number, number, Version> | null = null;
421
- let totalSize = 0;
422
- // let fileCount = 0;
423
- for await (const { path,size, cid } of results) {
424
- // The entry with path equal to the root folder name is our root
425
- if (path === rootFolderName) {
426
- rootCID = cid;
427
- totalSize = Number(size);
428
- }
429
- }
430
- if (!rootCID) {
431
- console.error("Failed to find root directory CID in IPFS results");
432
- return [null, new Error("Failed to find root directory CID")];
433
- }
434
- // 获取rootCID下的块数量
435
-
436
- const fileCount = await this.countDirectoryBlocks(rootCID);
437
-
438
-
439
- // Get final node and CID
440
- const finalCid = rootCID.toString();
441
- const folderSize = totalSize;
442
-
443
- // Sign folder data
444
- const serverPidBytes = new TextEncoder().encode(peerId);
445
- const sizeValue = uint64ToLittleEndianBytes(folderSize);
446
- const heightValue = uint32ToLittleEndianBytes(blockHeight || 0);
447
- const typeValue = uint32ToLittleEndianBytes(1); // Folder type = 1
448
-
449
- // Create signature payload
450
- let preSign = new TextEncoder().encode(finalCid);
451
- preSign = mergeUInt8Arrays(preSign, sizeValue) as any;
452
- preSign = mergeUInt8Arrays(preSign, heightValue) as any;
453
- preSign = mergeUInt8Arrays(preSign, typeValue) as any;
454
- preSign = mergeUInt8Arrays(preSign, serverPidBytes) as any;
455
-
456
- // Sign the data
457
- const signature = await this.context.sign(preSign);
458
-
459
-
460
-
461
-
462
- // Create file options
463
- const fileOptions = {
464
- signature,
465
- blockHeight: blockHeight || 0,
466
- fileSize: folderSize,
467
- fileCount: fileCount,
468
- pubkey: this.context.getPubkeyRaw(),
469
- vaccount: "",
470
- };
471
-
472
- if (vaccount) {
473
- fileOptions.vaccount = vaccount;
474
- }
475
- let client = this.connectedDc.client;
476
- if (client === null) {
477
- return ["", new Error("ErrConnectToAccountPeersFail")];
478
- }
479
-
480
- if (client.peerAddr === null) {
481
- return ["", new Error("ErrConnectToAccountPeersFail")];
482
- }
483
- if(!this.context.publicKey){
484
- return [null, Errors.ErrPublicKeyIsNull];
485
- }
486
- if (client.token == "") {
487
- await client.GetToken(
488
- this.context.appInfo.appId || "",
489
- this.context.publicKey.string(),
490
- this.context.sign
491
- );
492
- }
493
-
494
- // Store folder on DC network
495
- const fileClient = new FileClient(
496
- this.connectedDc.client,
497
- this.dcNodeClient,
498
- this.context
499
- );
500
-
501
- let resFlag = false;
502
- let resStatus = 0;
503
- let resError = null;
504
- // Create channel for async communication
505
- fileClient.storeFolder(
506
- rootCID.toString(),
507
- fileOptions,
508
- (status: UploadStatus, total: number, processed: number): void => {
509
- resFlag = true;
510
- resStatus = status;
511
- updateTransmitCount(status, total, processed);
512
- },
513
- async (error: Error) => {
514
- resFlag = true;
515
- resError = error;
516
- }
517
- );
518
- while (!resFlag) {
519
- await sleep(100);
520
- }
521
- if (resError) {
522
- updateTransmitCount(UploadStatus.ERROR, 0, 0);
523
- return [null, resError];
524
- }
525
- if (resStatus === UploadStatus.ERROR || resStatus === UploadStatus.ABNORMAL
526
- || resStatus === UploadStatus.NOSPACE || resStatus === UploadStatus.FILECOUNTERROR
527
- || resStatus === UploadStatus.FILESIZEERROR || resStatus === UploadStatus.PULLERROR) {
528
- //上传失败的时候,不需要操作
529
- return [null, Errors.ErrNoNeedUpload];
530
- }
531
- //创建文件主动上报流
532
- await this.dc.createTransferStream(
533
- this.dcNodeClient.libp2p,
534
- this.dcNodeClient.blockstore,
535
- nodeAddr,
536
- BrowserType.File,
537
- finalCid
538
- );
539
- return [finalCid, null];
540
- } catch (error) {
541
- console.error("Folder upload failed:", error);
542
- return [null, error instanceof Error ? error : new Error(String(error))];
543
- }
544
- }
545
-
546
- /**
547
- * Creates a custom FileList object from file paths and contents
548
- * @param filesMap - Map of file paths to content (string or Uint8Array)
549
- * @param rootFolderName - Optional root folder name (defaults to "upload")
550
- * @returns A FileList-like object that can be used with addFolder
551
- */
552
- createCustomFileList(
553
- filesMap:
554
- | Map<string, string | Uint8Array | ArrayBuffer>
555
- | Record<string, string | Uint8Array | ArrayBuffer>,
556
- rootFolderName: string = "upload"
557
- ): FileList {
558
- // Convert object to Map if needed
559
- const filesMapObj =
560
- filesMap instanceof Map ? filesMap : new Map(Object.entries(filesMap));
561
-
562
- // Create File objects with proper webkitRelativePath
563
- const files: File[] = [];
564
-
565
- filesMapObj.forEach((content, path) => {
566
- // Ensure path starts with rootFolderName
567
- const fullPath = path.startsWith(rootFolderName)
568
- ? path
569
- : `${rootFolderName}/${path}`;
570
-
571
- // Convert content to proper format
572
- let fileContent: Blob;
573
- if (typeof content === "string") {
574
- fileContent = new Blob([content], { type: "text/plain" });
575
- } else {
576
- fileContent = new Blob([content as any]);
577
- }
578
-
579
- // Create File object with webkitRelativePath
580
- // const file = new File([fileContent], path.split("/").pop() || "unnamed", {
581
- // type: "application/octet-stream",
582
- // }) as File & { webkitRelativePath: string };
583
- const file = Object.defineProperty(
584
- new File([fileContent], path.split("/").pop() || "unnamed", {
585
- type: "application/octet-stream",
586
- }),
587
- "webkitRelativePath",
588
- {
589
- value: fullPath,
590
- writable: false
591
- }
592
- );
593
-
594
- // Set webkitRelativePath property
595
- // file.webkitRelativePath = fullPath;
596
-
597
- files.push(file);
598
- });
599
-
600
- // Create a FileList-like object
601
- const fileListObj: Record<string, any> = {
602
- length: files.length,
603
- item(index: number): File {
604
- if (index < 0 || index >= files.length) {
605
- return null as any; // Return null for out-of-bounds index
606
- }
607
- return files[index]!;
608
- },
609
- [Symbol.iterator](): Iterator<File> {
610
- let index = 0;
611
- return {
612
- next(): IteratorResult<File> {
613
- if (index < files.length) {
614
- return { value: files[index++]!, done: false };
615
- } else {
616
- return { value: null as any, done: true };
617
- }
618
- },
619
- };
620
- },
621
- };
622
-
623
- // Add files with their indices as keys
624
- files.forEach((file, index) => {
625
- fileListObj[index] = file;
626
- });
627
-
628
- const fileList = fileListObj as unknown as FileList;
629
-
630
- return fileList;
631
- }
632
-
633
- private fileToStream(file: File, enkey: string): ReadableStream<Uint8Array> {
634
- const symKey = enkey ? SymmetricKey.fromString(enkey) : null;
635
- const { readable, writable } = new TransformStream<
636
- Uint8Array,
637
- Uint8Array
638
- >();
639
- const writer = writable.getWriter();
640
- const fileReader = new FileReader();
641
- const chunkSize = 3 <<20; // 3MB chunks
642
- let offset = 0;
643
- const processFile = async () => {
644
- try {
645
- while (offset < file.size) {
646
- // Read chunk
647
- const chunk = file.slice(offset, offset + chunkSize);
648
- const buffer = await new Promise<ArrayBuffer>((resolve, reject) => {
649
- fileReader.onload = () => resolve(fileReader.result as ArrayBuffer);
650
- fileReader.onerror = reject;
651
- fileReader.readAsArrayBuffer(chunk);
652
- });
653
- const content = new Uint8Array(buffer);
654
- // Encrypt if needed
655
- if (symKey && file.name !== "dc_ownuser") {
656
- const encrypted = await symKey.encrypt(content);
657
- await writer.write(encrypted);
658
- } else {
659
- await writer.write(content);
660
- }
661
- offset += chunkSize;
662
- }
663
- } catch (error) {
664
- console.error("Error processing file:", error);
665
- } finally {
666
- await writer.close();
667
- }
668
- };
669
- // Start processing the file
670
- processFile().catch((error) => {
671
- console.error("Error in file processing:", error);
672
- });
673
- return readable;
674
- }
675
- /**
676
- * Counts all blocks recursively in a directory structure
677
- * @param rootCID - The CID of the root directory
678
- * @returns Promise with the total block count
679
- */
680
- async countDirectoryBlocks(rootCID: CID): Promise<number> {
681
- try {
682
- const fs = unixfs(this.dcNodeClient);
683
- let totalBlocks = 0;
684
-
685
- // Get stats for the root directory itself
686
- const rootStats = await fs.stat(rootCID);
687
- totalBlocks += Number(rootStats.blocks || 0);
688
-
689
- // List all entries in the directory and process them recursively
690
- for await (const entry of fs.ls(rootCID)) {
691
- const { cid, type } = entry;
692
-
693
- // Get stats for the current entry
694
- const stats = await fs.stat(cid);
695
-
696
- if (type === "directory") {
697
- // Recursively count blocks in subdirectories
698
- const subDirBlocks = await this.countDirectoryBlocks(cid);
699
- totalBlocks += subDirBlocks;
700
- } else {
701
- // For files, add their block count
702
- totalBlocks += Number(stats.blocks || 0);
703
- }
704
- }
705
-
706
- console.log(`Directory ${rootCID.toString()} contains ${totalBlocks} blocks`);
707
- return totalBlocks;
708
- } catch (error) {
709
- console.error("Error counting directory blocks:", error);
710
- throw error;
711
- }
712
- }
713
- /**
714
- * Extract the root folder name from a FileList
715
- */
716
- private extractRootFolderName(files: FileList): string {
717
- if (files.length === 0) return "root";
718
-
719
- const path = files[0]!.webkitRelativePath || files[0]!.name;
720
- const parts = path.split("/");
721
-
722
- return parts[0] || "root";
723
- }
724
-
725
- /**
726
- * Create a folder in IPFS
727
- */
728
- private async createFolderInIpfs(folderName: string): Promise<CID> {
729
- const fs = unixfs(this.dcNodeClient);
730
- const dirCid = await fs.addDirectory({
731
- path: folderName,
732
- });
733
- return dirCid;
734
- }
735
-
736
- /**
737
- * Calculate the size of all files in a folder
738
- */
739
- private calculateFolderSize(files: FileList): number {
740
- let totalSize = 0;
741
- for (let i = 0; i < files.length; i++) {
742
- totalSize += files[i]!.size;
743
- }
744
- return totalSize;
745
- }
746
-
747
- /**
748
- * Adds a file to MFS file system folder with optional encryption
749
- * @param parentDir - The parent directory in MFS
750
- * @param dirPath - The directory path
751
- * @param fileName - The name of the file
752
- * @param enkey - Optional encryption key
753
- * @returns Promise with result or error
754
- */
755
- async addFileToMfsFolder(
756
- parentDir: any, // MFS Directory type (replace with actual type)
757
- dirPath: string,
758
- fileName: File,
759
- enkey: string
760
- ): Promise<[string | null, Error | null]> {
761
- let symKey: SymmetricKey | null = null;
762
- const readPath = `${dirPath}/${fileName}`; // Use path joining appropriate for your env
763
-
764
- try {
765
- // Open file (implementation depends on environment - browser vs Node.js)
766
- const fileContent = await this.readFile(readPath);
767
- if (!fileContent) {
768
- return [null, new Error("Could not open file")];
769
- }
770
-
771
- // Create encryption key if needed
772
- if (enkey !== "") {
773
- symKey = SymmetricKey.fromString(enkey);
774
- }
775
-
776
- // Create readable and writable streams for processing
777
- const { readable, writable } = new TransformStream<
778
- Uint8Array,
779
- Uint8Array
780
- >();
781
- const writer = writable.getWriter();
782
-
783
- // Process the file in a separate async function (equivalent to goroutine)
784
- (async () => {
785
- try {
786
- const file = await fetch(readPath).then((r) => r.blob());
787
- const fileReader = new FileReader();
788
- const chunkSize = 1024;
789
- let offset = 0;
790
-
791
- while (offset < file.size) {
792
- // Check for abort signal if needed
793
- // if (signal?.aborted) throw new Error("Operation cancelled");
794
-
795
- // Read chunk
796
- const chunk = file.slice(offset, offset + chunkSize);
797
- const buffer = await new Promise<ArrayBuffer>((resolve, reject) => {
798
- fileReader.onload = () =>
799
- resolve(fileReader.result as ArrayBuffer);
800
- fileReader.onerror = reject;
801
- fileReader.readAsArrayBuffer(chunk);
802
- });
803
-
804
- const content = new Uint8Array(buffer);
805
-
806
- // Encrypt if needed
807
- if (symKey) {
808
- const encrypted = await symKey.encrypt(content);
809
- await writer.write(encrypted);
810
- } else {
811
- await writer.write(content);
812
- }
813
-
814
- offset += chunkSize;
815
- }
816
- } catch (error) {
817
- console.error("Error processing file:", error);
818
- } finally {
819
- await writer.close();
820
- }
821
- })();
822
-
823
- // Add file to IPFS
824
- const fs = unixfs(this.dcNodeClient);
825
- const cid = await fs.addByteStream(this.streamToAsyncIterable(readable), {
826
- rawLeaves: false,
827
- leafType: "file",
828
- shardSplitThresholdBytes: 256 * 1024,
829
- });
830
-
831
- // Add to parent directory in MFS
832
- await parentDir.addChild(fileName, cid);
833
- await parentDir.flush();
834
-
835
- return [cid.toString(), null];
836
- } catch (error) {
837
- return [null, error instanceof Error ? error : new Error(String(error))];
838
- }
839
- }
840
-
841
- // Helper method to convert a ReadableStream to AsyncIterable
842
- private async *streamToAsyncIterable(
843
- stream: ReadableStream<Uint8Array>
844
- ): AsyncGenerator<Uint8Array> {
845
- const reader = stream.getReader();
846
- try {
847
- while (true) {
848
- const { done, value } = await reader.read();
849
- if (done) break;
850
- yield value;
851
- }
852
- } finally {
853
- reader.releaseLock();
854
- }
855
- }
856
-
857
- // Helper to read a file (implementation depends on environment)
858
- private async readFile(path: string): Promise<Blob | null> {
859
- try {
860
- // Browser implementation example - replace with appropriate method
861
- const response = await fetch(path);
862
- return await response.blob();
863
- } catch (error) {
864
- console.error("Error reading file:", error);
865
- return null;
866
- }
867
- }
868
-
869
- private async *chunkGenerator(stream: Stream): AsyncGenerator<Uint8Array> {
870
- const iterator = stream.source[Symbol.asyncIterator]();
871
- while (true) {
872
- try {
873
- const { done, value } = await iterator.next();
874
- if (done) break;
875
- const res = value instanceof Uint8ArrayList ? value.subarray() : value;
876
- yield res;
877
- } catch (err) {
878
- console.error("chunkGenerator error:", err);
879
- }
880
- }
881
- }
882
-
883
- /**
884
- * 组装 CustomMessage 数据到 Uint8Array
885
- * @param message - CustomMessage 包含消息的基本结构
886
- * @returns Uint8Array - 序列化后的数据
887
- */
888
- assembleCustomMessage(message: CustomMessage): Uint8Array {
889
- // Step 1: header部分(1字节类型 + 2字节版本号 + 4字节payload长度)
890
- const headerLength = 7; // Header固定长度:1字节Type + 2字节Version + 4字节Payload长度
891
- const payloadLength = message.payload.byteLength;
892
-
893
- const buffer = new Uint8Array(headerLength + payloadLength);
894
-
895
- buffer[0] = message.type;
896
- buffer[1] = (message.version >> 8) & 0xff;
897
- buffer[2] = message.version & 0xff;
898
- buffer[3] = (payloadLength >> 24) & 0xff;
899
- buffer[4] = (payloadLength >> 16) & 0xff;
900
- buffer[5] = (payloadLength >> 8) & 0xff;
901
- buffer[6] = payloadLength & 0xff;
902
-
903
- // Step 5: 设置 Payload 数据
904
- buffer.set(message.payload, headerLength);
905
-
906
- return buffer;
907
- }
908
- parseMessage(
909
- data: Uint8Array
910
- ): { type: number; version: number; payload: Uint8Array } | null {
911
- if (data.length < 7) {
912
- return null;
913
- }
914
-
915
- // 第 1 字节: 消息类型
916
- const type = data[0]!;
917
-
918
- // 第 2 和 3 字节: 版本号(大端序)
919
- const version = (data[1]! << 8) | data[2]!; // 手动处理大端序
920
-
921
- // 第 4 至 7 字节: payload 长度(大端序)
922
- const payloadLength =
923
- (data[3]! << 24) | (data[4]! << 16) | (data[5]! << 8) | data[6]!;
924
-
925
- // 验证数据完整性
926
- if (data.length < 7 + payloadLength) {
927
- return null;
928
- }
929
-
930
- // 提取 payload
931
- const payload = data.slice(7, 7 + payloadLength); // 提取负载数据
932
-
933
- return {
934
- type,
935
- version,
936
- payload,
937
- };
938
- }
939
-
940
-
941
-
942
- /**
943
- * 获取文件夹下的所有文件,包括内容(支持多级目录递归)
944
- * @param cid 根目录的CID
945
- * @param decryptKey 解密密钥
946
- * @param recursive 是否递归获取子目录,默认false(保持向后兼容)
947
- * @returns 文件列表:[{Name:文件或目录名,Type:0-文件 1-目录,Size:大小,Hash:文件或目录cid,Path:完整路径}]
948
- */
949
- async getFolderFileListWithContent(
950
- cid: string,
951
- decryptKey: string,
952
- recursive: boolean = true
953
- ): Promise<[Array<{Name: string; Type: number; Size: number; Hash: string; Path: string, Content?: Uint8Array}> | null, Error | null]> {
954
- const [fileList, err] = await this.getFolderFileList(cid, cidNeedConnect.NEED, recursive);
955
- if (err || !fileList) return [null, err];
956
- for (let i = 0; i < fileList.length; i++) {
957
- const file = fileList[i];
958
- if (file?.Type === 0) {
959
- if (file?.Name == "dc_ownuser") {
960
- continue;
961
- }
962
- const content = await this.getFileFromDc(file.Hash, decryptKey,cidNeedConnect.NOT_NEED,true); //和目录同节点,无需连接
963
- if (!content) {
964
- return [null, Errors.ErrNoFileChose];
965
- }
966
- file.Content = content;
967
- }
968
- }
969
- return [fileList, null];
970
- }
971
-
972
-
973
-
974
- /**
975
- * 获取文件夹下的文件列表(支持多级目录递归)
976
- * @param cid 根目录的CID
977
- * @param flag 是否需要连接节点
978
- * @param recursive 是否递归获取子目录,默认false(保持向后兼容)
979
- * @returns 返回JSON格式的文件列表:[{Name:文件或目录名,Type:0-文件 1-目录,Size:大小,Hash:文件或目录cid,Path:完整路径}]
980
- */
981
- async getFolderFileList(
982
- cid: string,
983
- flag?: number,
984
- recursive: boolean = true
985
- ): Promise<[Array<{Name: string; Type: number; Size: number; Hash: string; Path: string, Content?: Uint8Array}> | null, Error | null]> {
986
- try {
987
- const id = CID.parse(cid);
988
- if (flag !== cidNeedConnect.NOT_NEED) {
989
- const [multiAddrs, peers] = await this.dc?._connectToObjNodes(cid);
990
- if (!multiAddrs && peers) {
991
- // 有peers但是没有multiaddrs
992
- return [null, Errors.ErrNoDcPeerConnected];
993
- }
994
- }
995
-
996
- const fs = unixfs(this.dcNodeClient);
997
- const fileNodes: Array<{
998
- Name: string;
999
- Type: number;
1000
- Size: number;
1001
- Hash: string;
1002
- Path: string;
1003
- Content?: Uint8Array; // 可选内容字段,用于存储文件内容
1004
- }> = [];
1005
-
1006
- // 递归获取目录内容的内部函数
1007
- const traverseDirectory = async (dirCid: CID, currentPath: string = '') => {
1008
- // 遍历当前目录内容
1009
- for await (const entry of fs.ls(dirCid)) {
1010
- const { name, cid, type } = entry;
1011
- if (name === '.' || name === '..' || name === 'dc_ownuser') {
1012
- continue; // 跳过当前目录和上级目录
1013
- }
1014
- // 构建完整路径
1015
- const fullPath = currentPath ? `${currentPath}/${name}` : name;
1016
-
1017
- // 获取文件/目录的统计信息
1018
- const stats = await fs.stat(cid);
1019
-
1020
- // 构造文件信息对象
1021
- const fileInfo = {
1022
- Name: name,
1023
- Type: type === 'directory' ? 1 : 0, // 0-文件 1-目录
1024
- Size: type === 'directory' ? 0 : Number(stats.fileSize || 0),
1025
- Hash: cid.toString(),
1026
- Path: fullPath
1027
- };
1028
-
1029
- fileNodes.push(fileInfo);
1030
-
1031
- // 如果是目录且需要递归,则继续遍历子目录
1032
- if (type === 'directory' && recursive) {
1033
- await traverseDirectory(cid, fullPath);
1034
- }
1035
- }
1036
- };
1037
-
1038
- // 开始遍历
1039
- await traverseDirectory(id);
1040
- return [fileNodes, null];
1041
- } catch (error) {
1042
- console.error('获取文件夹列表失败:', error);
1043
- return [null, error instanceof Error ? error : new Error(String(error))];
1044
- }
1045
- }
1046
-
1047
-
1048
-
1049
- // 从dc网络获取指定文件
1050
- // flag 是否需要连接节点,0-需要,1-不需要
1051
- getFileFromDc = async (cid: string, decryptKey: string, flag?: number,folderFlag?:boolean) : Promise<Uint8Array | null> => {
1052
- if (flag !== cidNeedConnect.NOT_NEED) {
1053
- const [multiAddrs, peers] = await this.dc?._connectToObjNodes(cid);
1054
- if (!multiAddrs && peers) {
1055
- // 有peers但是没有multiaddrs
1056
- return null;
1057
- }
1058
- // 没有peers
1059
- if (!peers) {
1060
- try {
1061
- const timeoutPromise = new Promise((resolve) => setTimeout(resolve, 5000, 'Timeout'));
1062
- const getPromise = this.getFileFromDcContent(cid, decryptKey,folderFlag);
1063
- const result = await Promise.race([timeoutPromise, getPromise]);
1064
- if(result === 'Timeout') {
1065
- return null;
1066
- }
1067
- if(result) {
1068
- return result as Uint8Array;
1069
- }
1070
- return null;
1071
- } catch (error) {
1072
- return null;
1073
- }
1074
- }
1075
- }
1076
- return this.getFileFromDcContent(cid, decryptKey,folderFlag);
1077
- };
1078
-
1079
- getFileFromDcContent = async (cid: string, decryptKey: string, folderFlag?:boolean): Promise<Uint8Array | null> => {
1080
- const fs = unixfs(this.dcNodeClient);
1081
- let headDealed = false;
1082
- let waitBuffer = new Uint8Array(0);
1083
- let fileContent = new Uint8Array(0);
1084
-
1085
- const encryptextLen = (3 << 20) + NonceBytes + TagBytes; //每段密文长度(最后一段可能会短一点)
1086
- const catOptions = {
1087
- offset: 0,
1088
- length: 32,
1089
- // signal: AbortSignal.timeout(5000),
1090
- };
1091
- let readCount = 0;
1092
- try {
1093
- for (;;) {
1094
- if (!headDealed && !folderFlag) {
1095
- const headBuf = await toBuffer(fs.cat(CID.parse(cid), catOptions));
1096
- readCount += headBuf.length;
1097
- if (headBuf.length > 0) {
1098
- waitBuffer = mergeUInt8Arrays(waitBuffer, headBuf) as any;
1099
- if (waitBuffer.length < 32) {
1100
- catOptions.offset = waitBuffer.length;
1101
- catOptions.length = 32 - waitBuffer.length;
1102
- continue;
1103
- } else {
1104
- //判断是否是dc网络存储的文件头
1105
- headDealed = true;
1106
- if (
1107
- compareByteArrays(
1108
- waitBuffer.subarray(0, 10),
1109
- Buffer.from("$$dcfile$$")
1110
- )
1111
- ) {
1112
- //判断是否是dc网络存储的文件头
1113
- waitBuffer = waitBuffer.subarray(32, waitBuffer.length);
1114
- }
1115
- }
1116
- } else {
1117
- if (waitBuffer.length > 0) {
1118
- if (decryptKey != "") {
1119
- const decrypted = await decryptContent(waitBuffer, decryptKey);
1120
- fileContent = mergeUInt8Arrays(fileContent, decrypted) as any;
1121
- } else {
1122
- fileContent = mergeUInt8Arrays(fileContent, waitBuffer) as any;
1123
- }
1124
- }
1125
- break;
1126
- }
1127
- continue;
1128
- }
1129
- catOptions.offset = readCount;
1130
- catOptions.length = encryptextLen;
1131
- const buf = await toBuffer(fs.cat(CID.parse(cid), catOptions));
1132
- if (buf.length > 0) {
1133
- readCount += buf.length;
1134
- }
1135
- if (buf.length > 0) {
1136
- waitBuffer = mergeUInt8Arrays(waitBuffer, buf) as any;
1137
- while (waitBuffer.length >= encryptextLen) {
1138
- const encryptBuffer = waitBuffer.subarray(0, encryptextLen);
1139
- waitBuffer = waitBuffer.subarray(encryptextLen, waitBuffer.length);
1140
- if (decryptKey == "") {
1141
- fileContent = mergeUInt8Arrays(fileContent, encryptBuffer) as any;
1142
- continue;
1143
- }
1144
- //解密
1145
- const decrypted = await decryptContent(encryptBuffer, decryptKey);
1146
- fileContent = mergeUInt8Arrays(fileContent, decrypted) as any;
1147
- }
1148
- } else {
1149
- if (waitBuffer.length > 0) {
1150
- if (decryptKey != "") {
1151
- const decrypted = await decryptContent(waitBuffer, decryptKey);
1152
- fileContent = mergeUInt8Arrays(fileContent, decrypted) as any;
1153
- } else {
1154
- fileContent = mergeUInt8Arrays(fileContent, waitBuffer) as any;
1155
- }
1156
- }
1157
- break;
1158
- }
1159
- }
1160
- return fileContent;
1161
- } catch (error) {
1162
- console.error("getFileFromDc error", error);
1163
- return null;
1164
- }
1165
- }
1166
- /**
1167
- * 创建可随机访问的文件流
1168
- */
1169
- async createSeekableFileStream(
1170
- cid: string,
1171
- decryptKey: string,
1172
- flag?: number
1173
- ): Promise<SeekableFileStream | null> {
1174
- // 连接到节点
1175
- if (flag !== cidNeedConnect.NOT_NEED) {
1176
- const [multiAddrs, peers] = await this.dc?._connectToObjNodes(cid);
1177
- if (!multiAddrs && peers) {
1178
- // 有peers但是没有multiaddrs
1179
- return null;
1180
- }
1181
- }
1182
-
1183
- const fs = unixfs(this.dcNodeClient);
1184
-
1185
- try {
1186
- // 读取头信息
1187
- const headerData = await toBuffer(
1188
- fs.cat(CID.parse(cid), {
1189
- offset: 0,
1190
- length: 32,
1191
- })
1192
- );
1193
-
1194
- // 检查是否有DC文件头
1195
- const hasHeader = compareByteArrays(
1196
- headerData.subarray(0, 10),
1197
- Buffer.from(dcFileHead)
1198
- );
1199
-
1200
- // 获取文件大小
1201
- // const stats = await fs.stat(CID.parse(cid));
1202
- // const totalSize = Number(stats.fileSize);
1203
- const fileSize = this.readUint64BE(headerData, 24);
1204
-
1205
- const fileInfo = {
1206
- size: fileSize,
1207
- hasHeader,
1208
- headerSize: hasHeader ? 32 : 0,
1209
- };
1210
-
1211
- // 创建并返回流对象
1212
- return new SeekableFileStream({
1213
- fileInfo,
1214
- fs,
1215
- cid: CID.parse(cid),
1216
- decryptKey: decryptKey || "",
1217
- encryptChunkSize: (3 << 20) + NonceBytes + TagBytes,
1218
- });
1219
- } catch (err) {
1220
- console.error("Failed to create seekable file stream:", err);
1221
- return null;
1222
- }
1223
- }
1224
-
1225
- readUint64BE(buffer: Uint8Array, offset: number): number {
1226
- // JavaScript中Number可以安全表示的最大整数是2^53-1
1227
- const high =
1228
- buffer[offset]! * 2 ** 24 +
1229
- buffer[offset + 1]! * 2 ** 16 +
1230
- buffer[offset + 2]! * 2 ** 8 +
1231
- buffer[offset + 3]!;
1232
- const low =
1233
- buffer[offset + 4]! * 2 ** 24 +
1234
- buffer[offset + 5]! * 2 ** 16 +
1235
- buffer[offset + 6]! * 2 ** 8 +
1236
- buffer[offset + 7]!;
1237
-
1238
- return high * 2 ** 32 + low;
1239
- }
1240
- }