@alibarbar/common 1.1.3 → 1.1.5

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.
@@ -148,6 +148,8 @@ declare class ChunkUploader {
148
148
  private uploadedChunks;
149
149
  private status;
150
150
  private abortController;
151
+ /** 进度流专用 AbortController,用于在上传结束/取消时关闭流 */
152
+ private progressStreamAbortController;
151
153
  constructor(file: File, options?: UploadOptions);
152
154
  /**
153
155
  * 初始化上传
@@ -174,9 +176,13 @@ declare class ChunkUploader {
174
176
  */
175
177
  private uploadChunksConcurrently;
176
178
  /**
177
- * 更新上传进度(仅用于回调前端显示)
179
+ * 启动进度流式接口(单次请求,通过 ReadableStream 消费多条 SSE 事件)
178
180
  */
179
- private updateProgress;
181
+ private startProgressStream;
182
+ /**
183
+ * 关闭进度流
184
+ */
185
+ private closeProgressStream;
180
186
  /**
181
187
  * 获取上传进度
182
188
  */
@@ -233,4 +239,4 @@ declare function createUploader(file: File, options?: UploadOptions): ChunkUploa
233
239
  */
234
240
  declare function uploadFile(file: File, options?: UploadOptions): Promise<CompleteUploadResponse>;
235
241
 
236
- export { type ApiResponse as A, type ChunkInfo as C, type UploadInitRequest as U, type ChunkUploadResponse as a, ChunkUploader as b, type CompleteUploadResponse as c, type UploadInitResponse as d, type UploadOptions as e, type UploadProgress as f, UploadStatus as g, createUploader as h, uploadFile as u };
242
+ export { type ApiResponse as A, ChunkUploader as C, type UploadInitRequest as U, type UploadInitResponse as a, type ChunkUploadResponse as b, createUploader as c, type CompleteUploadResponse as d, type UploadProgress as e, type UploadOptions as f, UploadStatus as g, type ChunkInfo as h, uploadFile as u };
@@ -148,6 +148,8 @@ declare class ChunkUploader {
148
148
  private uploadedChunks;
149
149
  private status;
150
150
  private abortController;
151
+ /** 进度流专用 AbortController,用于在上传结束/取消时关闭流 */
152
+ private progressStreamAbortController;
151
153
  constructor(file: File, options?: UploadOptions);
152
154
  /**
153
155
  * 初始化上传
@@ -174,9 +176,13 @@ declare class ChunkUploader {
174
176
  */
175
177
  private uploadChunksConcurrently;
176
178
  /**
177
- * 更新上传进度(仅用于回调前端显示)
179
+ * 启动进度流式接口(单次请求,通过 ReadableStream 消费多条 SSE 事件)
178
180
  */
179
- private updateProgress;
181
+ private startProgressStream;
182
+ /**
183
+ * 关闭进度流
184
+ */
185
+ private closeProgressStream;
180
186
  /**
181
187
  * 获取上传进度
182
188
  */
@@ -233,4 +239,4 @@ declare function createUploader(file: File, options?: UploadOptions): ChunkUploa
233
239
  */
234
240
  declare function uploadFile(file: File, options?: UploadOptions): Promise<CompleteUploadResponse>;
235
241
 
236
- export { type ApiResponse as A, type ChunkInfo as C, type UploadInitRequest as U, type ChunkUploadResponse as a, ChunkUploader as b, type CompleteUploadResponse as c, type UploadInitResponse as d, type UploadOptions as e, type UploadProgress as f, UploadStatus as g, createUploader as h, uploadFile as u };
242
+ export { type ApiResponse as A, ChunkUploader as C, type UploadInitRequest as U, type UploadInitResponse as a, type ChunkUploadResponse as b, createUploader as c, type CompleteUploadResponse as d, type UploadProgress as e, type UploadOptions as f, UploadStatus as g, type ChunkInfo as h, uploadFile as u };
package/dist/index.cjs CHANGED
@@ -1148,6 +1148,8 @@ var ChunkUploader = class {
1148
1148
  this.uploadedChunks = /* @__PURE__ */ new Set();
1149
1149
  this.status = "pending" /* PENDING */;
1150
1150
  this.abortController = null;
1151
+ /** 进度流专用 AbortController,用于在上传结束/取消时关闭流 */
1152
+ this.progressStreamAbortController = null;
1151
1153
  this.file = file;
1152
1154
  this.options = {
1153
1155
  chunkSize: options.chunkSize || 2 * 1024 * 1024,
@@ -1255,7 +1257,6 @@ var ChunkUploader = class {
1255
1257
  });
1256
1258
  if (response.code === 200 && response.data.success) {
1257
1259
  this.uploadedChunks.add(chunkInfo.index);
1258
- await this.updateProgress();
1259
1260
  } else {
1260
1261
  throw new Error(response.message || "\u5206\u7247\u4E0A\u4F20\u5931\u8D25");
1261
1262
  }
@@ -1290,23 +1291,60 @@ var ChunkUploader = class {
1290
1291
  await Promise.all(tasks);
1291
1292
  }
1292
1293
  /**
1293
- * 更新上传进度(仅用于回调前端显示)
1294
+ * 启动进度流式接口(单次请求,通过 ReadableStream 消费多条 SSE 事件)
1294
1295
  */
1295
- async updateProgress() {
1296
+ startProgressStream() {
1296
1297
  if (!this.taskId) return;
1297
- try {
1298
- const response = await this.request(
1299
- `/api/files/common/progress/${this.taskId}`,
1300
- {
1301
- method: "GET",
1302
- headers: this.options.headers
1298
+ this.progressStreamAbortController = new AbortController();
1299
+ const url = `${this.options.baseURL}/api/files/common/progress/${this.taskId}/stream`;
1300
+ fetch(url, {
1301
+ method: "GET",
1302
+ headers: {
1303
+ Accept: "text/event-stream",
1304
+ ...this.options.headers
1305
+ },
1306
+ signal: this.progressStreamAbortController.signal
1307
+ }).then(async (response) => {
1308
+ if (!response.ok || !response.body) return;
1309
+ const reader = response.body.getReader();
1310
+ const decoder = new TextDecoder();
1311
+ let buffer = "";
1312
+ try {
1313
+ while (true) {
1314
+ const { done, value } = await reader.read();
1315
+ if (done) break;
1316
+ buffer += decoder.decode(value, { stream: true });
1317
+ const lines = buffer.split("\n");
1318
+ buffer = lines.pop() ?? "";
1319
+ for (const line of lines) {
1320
+ if (line.startsWith("data: ")) {
1321
+ const jsonStr = line.slice(6).trim();
1322
+ if (!jsonStr) continue;
1323
+ try {
1324
+ const apiRes = JSON.parse(jsonStr);
1325
+ if (apiRes.code === 200 && apiRes.data) {
1326
+ this.options.onProgress(apiRes.data);
1327
+ }
1328
+ } catch {
1329
+ }
1330
+ }
1331
+ }
1303
1332
  }
1304
- );
1305
- if (response.code === 200 && response.data) {
1306
- this.options.onProgress(response.data);
1333
+ } finally {
1334
+ reader.releaseLock();
1307
1335
  }
1308
- } catch (error) {
1309
- console.warn("\u83B7\u53D6\u4E0A\u4F20\u8FDB\u5EA6\u5931\u8D25:", error);
1336
+ }).catch((error) => {
1337
+ if (error?.name === "AbortError") return;
1338
+ console.warn("\u8FDB\u5EA6\u6D41\u5F0F\u63A5\u53E3\u5F02\u5E38:", error);
1339
+ });
1340
+ }
1341
+ /**
1342
+ * 关闭进度流
1343
+ */
1344
+ closeProgressStream() {
1345
+ if (this.progressStreamAbortController) {
1346
+ this.progressStreamAbortController.abort();
1347
+ this.progressStreamAbortController = null;
1310
1348
  }
1311
1349
  }
1312
1350
  /**
@@ -1379,6 +1417,7 @@ var ChunkUploader = class {
1379
1417
  this.options.onComplete(result);
1380
1418
  return result;
1381
1419
  }
1420
+ this.startProgressStream();
1382
1421
  console.log("[\u4E0A\u4F20\u6D41\u7A0B] 2. \u51C6\u5907\u5206\u7247");
1383
1422
  this.prepareChunks();
1384
1423
  const totalChunks = this.chunks.length;
@@ -1422,6 +1461,8 @@ var ChunkUploader = class {
1422
1461
  this.options.onError(err);
1423
1462
  console.error("[\u4E0A\u4F20\u6D41\u7A0B] \u274C \u4E0A\u4F20\u5931\u8D25:", err);
1424
1463
  throw err;
1464
+ } finally {
1465
+ this.closeProgressStream();
1425
1466
  }
1426
1467
  }
1427
1468
  /**
@@ -1448,6 +1489,7 @@ var ChunkUploader = class {
1448
1489
  * 取消上传
1449
1490
  */
1450
1491
  async cancel() {
1492
+ this.closeProgressStream();
1451
1493
  if (this.taskId && this.status === "uploading" /* UPLOADING */) {
1452
1494
  try {
1453
1495
  await this.request(`/api/files/common/cancel/${this.taskId}`, {
package/dist/index.d.mts CHANGED
@@ -8,7 +8,7 @@ export { ParsedUrl, buildUrl, getQueryParams, isAbsoluteUrl, joinUrl, normalizeU
8
8
  export { HSL, RGB, contrast, darken, hexToRgb, hslToRgb, lighten, mix, rgbToHex, rgbToHsl } from './color.mjs';
9
9
  export { CurrencyCode, Locale, TranslationDictionary, createTranslator, formatCurrencyI18n, formatDateI18n, formatNumberI18n, formatRelativeTime, getLocale, pluralize, translate } from './i18n.mjs';
10
10
  export { calculateBlobMD5, calculateFileMD5, formatFileSize, getFileExtension, getFileNameWithoutExtension, splitFileIntoChunks } from './file.mjs';
11
- export { A as ApiResponse, C as ChunkInfo, a as ChunkUploadResponse, b as ChunkUploader, c as CompleteUploadResponse, U as UploadInitRequest, d as UploadInitResponse, e as UploadOptions, f as UploadProgress, g as UploadStatus, h as createUploader, u as uploadFile } from './index-CqWtZgzO.mjs';
11
+ export { A as ApiResponse, h as ChunkInfo, b as ChunkUploadResponse, C as ChunkUploader, d as CompleteUploadResponse, U as UploadInitRequest, a as UploadInitResponse, f as UploadOptions, e as UploadProgress, g as UploadStatus, c as createUploader, u as uploadFile } from './index-j5EqxJaC.mjs';
12
12
  export { SecureStorage, SecureStorageOptions, secureStorage } from './storage.mjs';
13
13
  export { checkOnline, downloadFile, fetchWithRetry, fetchWithTimeout, request } from './network.mjs';
14
14
  export { $, $$, addClass, copyToClipboard, getElementOffset, getScrollPosition, getStyle, isInViewport, removeClass, scrollTo, setStyle, toggleClass } from './dom.mjs';
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export { ParsedUrl, buildUrl, getQueryParams, isAbsoluteUrl, joinUrl, normalizeU
8
8
  export { HSL, RGB, contrast, darken, hexToRgb, hslToRgb, lighten, mix, rgbToHex, rgbToHsl } from './color.js';
9
9
  export { CurrencyCode, Locale, TranslationDictionary, createTranslator, formatCurrencyI18n, formatDateI18n, formatNumberI18n, formatRelativeTime, getLocale, pluralize, translate } from './i18n.js';
10
10
  export { calculateBlobMD5, calculateFileMD5, formatFileSize, getFileExtension, getFileNameWithoutExtension, splitFileIntoChunks } from './file.js';
11
- export { A as ApiResponse, C as ChunkInfo, a as ChunkUploadResponse, b as ChunkUploader, c as CompleteUploadResponse, U as UploadInitRequest, d as UploadInitResponse, e as UploadOptions, f as UploadProgress, g as UploadStatus, h as createUploader, u as uploadFile } from './index-CqWtZgzO.js';
11
+ export { A as ApiResponse, h as ChunkInfo, b as ChunkUploadResponse, C as ChunkUploader, d as CompleteUploadResponse, U as UploadInitRequest, a as UploadInitResponse, f as UploadOptions, e as UploadProgress, g as UploadStatus, c as createUploader, u as uploadFile } from './index-j5EqxJaC.js';
12
12
  export { SecureStorage, SecureStorageOptions, secureStorage } from './storage.js';
13
13
  export { checkOnline, downloadFile, fetchWithRetry, fetchWithTimeout, request } from './network.js';
14
14
  export { $, $$, addClass, copyToClipboard, getElementOffset, getScrollPosition, getStyle, isInViewport, removeClass, scrollTo, setStyle, toggleClass } from './dom.js';
package/dist/index.js CHANGED
@@ -1139,6 +1139,8 @@ var ChunkUploader = class {
1139
1139
  this.uploadedChunks = /* @__PURE__ */ new Set();
1140
1140
  this.status = "pending" /* PENDING */;
1141
1141
  this.abortController = null;
1142
+ /** 进度流专用 AbortController,用于在上传结束/取消时关闭流 */
1143
+ this.progressStreamAbortController = null;
1142
1144
  this.file = file;
1143
1145
  this.options = {
1144
1146
  chunkSize: options.chunkSize || 2 * 1024 * 1024,
@@ -1246,7 +1248,6 @@ var ChunkUploader = class {
1246
1248
  });
1247
1249
  if (response.code === 200 && response.data.success) {
1248
1250
  this.uploadedChunks.add(chunkInfo.index);
1249
- await this.updateProgress();
1250
1251
  } else {
1251
1252
  throw new Error(response.message || "\u5206\u7247\u4E0A\u4F20\u5931\u8D25");
1252
1253
  }
@@ -1281,23 +1282,60 @@ var ChunkUploader = class {
1281
1282
  await Promise.all(tasks);
1282
1283
  }
1283
1284
  /**
1284
- * 更新上传进度(仅用于回调前端显示)
1285
+ * 启动进度流式接口(单次请求,通过 ReadableStream 消费多条 SSE 事件)
1285
1286
  */
1286
- async updateProgress() {
1287
+ startProgressStream() {
1287
1288
  if (!this.taskId) return;
1288
- try {
1289
- const response = await this.request(
1290
- `/api/files/common/progress/${this.taskId}`,
1291
- {
1292
- method: "GET",
1293
- headers: this.options.headers
1289
+ this.progressStreamAbortController = new AbortController();
1290
+ const url = `${this.options.baseURL}/api/files/common/progress/${this.taskId}/stream`;
1291
+ fetch(url, {
1292
+ method: "GET",
1293
+ headers: {
1294
+ Accept: "text/event-stream",
1295
+ ...this.options.headers
1296
+ },
1297
+ signal: this.progressStreamAbortController.signal
1298
+ }).then(async (response) => {
1299
+ if (!response.ok || !response.body) return;
1300
+ const reader = response.body.getReader();
1301
+ const decoder = new TextDecoder();
1302
+ let buffer = "";
1303
+ try {
1304
+ while (true) {
1305
+ const { done, value } = await reader.read();
1306
+ if (done) break;
1307
+ buffer += decoder.decode(value, { stream: true });
1308
+ const lines = buffer.split("\n");
1309
+ buffer = lines.pop() ?? "";
1310
+ for (const line of lines) {
1311
+ if (line.startsWith("data: ")) {
1312
+ const jsonStr = line.slice(6).trim();
1313
+ if (!jsonStr) continue;
1314
+ try {
1315
+ const apiRes = JSON.parse(jsonStr);
1316
+ if (apiRes.code === 200 && apiRes.data) {
1317
+ this.options.onProgress(apiRes.data);
1318
+ }
1319
+ } catch {
1320
+ }
1321
+ }
1322
+ }
1294
1323
  }
1295
- );
1296
- if (response.code === 200 && response.data) {
1297
- this.options.onProgress(response.data);
1324
+ } finally {
1325
+ reader.releaseLock();
1298
1326
  }
1299
- } catch (error) {
1300
- console.warn("\u83B7\u53D6\u4E0A\u4F20\u8FDB\u5EA6\u5931\u8D25:", error);
1327
+ }).catch((error) => {
1328
+ if (error?.name === "AbortError") return;
1329
+ console.warn("\u8FDB\u5EA6\u6D41\u5F0F\u63A5\u53E3\u5F02\u5E38:", error);
1330
+ });
1331
+ }
1332
+ /**
1333
+ * 关闭进度流
1334
+ */
1335
+ closeProgressStream() {
1336
+ if (this.progressStreamAbortController) {
1337
+ this.progressStreamAbortController.abort();
1338
+ this.progressStreamAbortController = null;
1301
1339
  }
1302
1340
  }
1303
1341
  /**
@@ -1370,6 +1408,7 @@ var ChunkUploader = class {
1370
1408
  this.options.onComplete(result);
1371
1409
  return result;
1372
1410
  }
1411
+ this.startProgressStream();
1373
1412
  console.log("[\u4E0A\u4F20\u6D41\u7A0B] 2. \u51C6\u5907\u5206\u7247");
1374
1413
  this.prepareChunks();
1375
1414
  const totalChunks = this.chunks.length;
@@ -1413,6 +1452,8 @@ var ChunkUploader = class {
1413
1452
  this.options.onError(err);
1414
1453
  console.error("[\u4E0A\u4F20\u6D41\u7A0B] \u274C \u4E0A\u4F20\u5931\u8D25:", err);
1415
1454
  throw err;
1455
+ } finally {
1456
+ this.closeProgressStream();
1416
1457
  }
1417
1458
  }
1418
1459
  /**
@@ -1439,6 +1480,7 @@ var ChunkUploader = class {
1439
1480
  * 取消上传
1440
1481
  */
1441
1482
  async cancel() {
1483
+ this.closeProgressStream();
1442
1484
  if (this.taskId && this.status === "uploading" /* UPLOADING */) {
1443
1485
  try {
1444
1486
  await this.request(`/api/files/common/cancel/${this.taskId}`, {
package/dist/upload.cjs CHANGED
@@ -46,6 +46,8 @@ var ChunkUploader = class {
46
46
  this.uploadedChunks = /* @__PURE__ */ new Set();
47
47
  this.status = "pending" /* PENDING */;
48
48
  this.abortController = null;
49
+ /** 进度流专用 AbortController,用于在上传结束/取消时关闭流 */
50
+ this.progressStreamAbortController = null;
49
51
  this.file = file;
50
52
  this.options = {
51
53
  chunkSize: options.chunkSize || 2 * 1024 * 1024,
@@ -153,7 +155,6 @@ var ChunkUploader = class {
153
155
  });
154
156
  if (response.code === 200 && response.data.success) {
155
157
  this.uploadedChunks.add(chunkInfo.index);
156
- await this.updateProgress();
157
158
  } else {
158
159
  throw new Error(response.message || "\u5206\u7247\u4E0A\u4F20\u5931\u8D25");
159
160
  }
@@ -188,23 +189,60 @@ var ChunkUploader = class {
188
189
  await Promise.all(tasks);
189
190
  }
190
191
  /**
191
- * 更新上传进度(仅用于回调前端显示)
192
+ * 启动进度流式接口(单次请求,通过 ReadableStream 消费多条 SSE 事件)
192
193
  */
193
- async updateProgress() {
194
+ startProgressStream() {
194
195
  if (!this.taskId) return;
195
- try {
196
- const response = await this.request(
197
- `/api/files/common/progress/${this.taskId}`,
198
- {
199
- method: "GET",
200
- headers: this.options.headers
196
+ this.progressStreamAbortController = new AbortController();
197
+ const url = `${this.options.baseURL}/api/files/common/progress/${this.taskId}/stream`;
198
+ fetch(url, {
199
+ method: "GET",
200
+ headers: {
201
+ Accept: "text/event-stream",
202
+ ...this.options.headers
203
+ },
204
+ signal: this.progressStreamAbortController.signal
205
+ }).then(async (response) => {
206
+ if (!response.ok || !response.body) return;
207
+ const reader = response.body.getReader();
208
+ const decoder = new TextDecoder();
209
+ let buffer = "";
210
+ try {
211
+ while (true) {
212
+ const { done, value } = await reader.read();
213
+ if (done) break;
214
+ buffer += decoder.decode(value, { stream: true });
215
+ const lines = buffer.split("\n");
216
+ buffer = lines.pop() ?? "";
217
+ for (const line of lines) {
218
+ if (line.startsWith("data: ")) {
219
+ const jsonStr = line.slice(6).trim();
220
+ if (!jsonStr) continue;
221
+ try {
222
+ const apiRes = JSON.parse(jsonStr);
223
+ if (apiRes.code === 200 && apiRes.data) {
224
+ this.options.onProgress(apiRes.data);
225
+ }
226
+ } catch {
227
+ }
228
+ }
229
+ }
201
230
  }
202
- );
203
- if (response.code === 200 && response.data) {
204
- this.options.onProgress(response.data);
231
+ } finally {
232
+ reader.releaseLock();
205
233
  }
206
- } catch (error) {
207
- console.warn("\u83B7\u53D6\u4E0A\u4F20\u8FDB\u5EA6\u5931\u8D25:", error);
234
+ }).catch((error) => {
235
+ if (error?.name === "AbortError") return;
236
+ console.warn("\u8FDB\u5EA6\u6D41\u5F0F\u63A5\u53E3\u5F02\u5E38:", error);
237
+ });
238
+ }
239
+ /**
240
+ * 关闭进度流
241
+ */
242
+ closeProgressStream() {
243
+ if (this.progressStreamAbortController) {
244
+ this.progressStreamAbortController.abort();
245
+ this.progressStreamAbortController = null;
208
246
  }
209
247
  }
210
248
  /**
@@ -277,6 +315,7 @@ var ChunkUploader = class {
277
315
  this.options.onComplete(result);
278
316
  return result;
279
317
  }
318
+ this.startProgressStream();
280
319
  console.log("[\u4E0A\u4F20\u6D41\u7A0B] 2. \u51C6\u5907\u5206\u7247");
281
320
  this.prepareChunks();
282
321
  const totalChunks = this.chunks.length;
@@ -320,6 +359,8 @@ var ChunkUploader = class {
320
359
  this.options.onError(err);
321
360
  console.error("[\u4E0A\u4F20\u6D41\u7A0B] \u274C \u4E0A\u4F20\u5931\u8D25:", err);
322
361
  throw err;
362
+ } finally {
363
+ this.closeProgressStream();
323
364
  }
324
365
  }
325
366
  /**
@@ -346,6 +387,7 @@ var ChunkUploader = class {
346
387
  * 取消上传
347
388
  */
348
389
  async cancel() {
390
+ this.closeProgressStream();
349
391
  if (this.taskId && this.status === "uploading" /* UPLOADING */) {
350
392
  try {
351
393
  await this.request(`/api/files/common/cancel/${this.taskId}`, {
package/dist/upload.d.mts CHANGED
@@ -1 +1 @@
1
- export { b as ChunkUploader, h as createUploader, u as uploadFile } from './index-CqWtZgzO.mjs';
1
+ export { C as ChunkUploader, c as createUploader, u as uploadFile } from './index-j5EqxJaC.mjs';
package/dist/upload.d.ts CHANGED
@@ -1 +1 @@
1
- export { b as ChunkUploader, h as createUploader, u as uploadFile } from './index-CqWtZgzO.js';
1
+ export { C as ChunkUploader, c as createUploader, u as uploadFile } from './index-j5EqxJaC.js';
package/dist/upload.js CHANGED
@@ -44,6 +44,8 @@ var ChunkUploader = class {
44
44
  this.uploadedChunks = /* @__PURE__ */ new Set();
45
45
  this.status = "pending" /* PENDING */;
46
46
  this.abortController = null;
47
+ /** 进度流专用 AbortController,用于在上传结束/取消时关闭流 */
48
+ this.progressStreamAbortController = null;
47
49
  this.file = file;
48
50
  this.options = {
49
51
  chunkSize: options.chunkSize || 2 * 1024 * 1024,
@@ -151,7 +153,6 @@ var ChunkUploader = class {
151
153
  });
152
154
  if (response.code === 200 && response.data.success) {
153
155
  this.uploadedChunks.add(chunkInfo.index);
154
- await this.updateProgress();
155
156
  } else {
156
157
  throw new Error(response.message || "\u5206\u7247\u4E0A\u4F20\u5931\u8D25");
157
158
  }
@@ -186,23 +187,60 @@ var ChunkUploader = class {
186
187
  await Promise.all(tasks);
187
188
  }
188
189
  /**
189
- * 更新上传进度(仅用于回调前端显示)
190
+ * 启动进度流式接口(单次请求,通过 ReadableStream 消费多条 SSE 事件)
190
191
  */
191
- async updateProgress() {
192
+ startProgressStream() {
192
193
  if (!this.taskId) return;
193
- try {
194
- const response = await this.request(
195
- `/api/files/common/progress/${this.taskId}`,
196
- {
197
- method: "GET",
198
- headers: this.options.headers
194
+ this.progressStreamAbortController = new AbortController();
195
+ const url = `${this.options.baseURL}/api/files/common/progress/${this.taskId}/stream`;
196
+ fetch(url, {
197
+ method: "GET",
198
+ headers: {
199
+ Accept: "text/event-stream",
200
+ ...this.options.headers
201
+ },
202
+ signal: this.progressStreamAbortController.signal
203
+ }).then(async (response) => {
204
+ if (!response.ok || !response.body) return;
205
+ const reader = response.body.getReader();
206
+ const decoder = new TextDecoder();
207
+ let buffer = "";
208
+ try {
209
+ while (true) {
210
+ const { done, value } = await reader.read();
211
+ if (done) break;
212
+ buffer += decoder.decode(value, { stream: true });
213
+ const lines = buffer.split("\n");
214
+ buffer = lines.pop() ?? "";
215
+ for (const line of lines) {
216
+ if (line.startsWith("data: ")) {
217
+ const jsonStr = line.slice(6).trim();
218
+ if (!jsonStr) continue;
219
+ try {
220
+ const apiRes = JSON.parse(jsonStr);
221
+ if (apiRes.code === 200 && apiRes.data) {
222
+ this.options.onProgress(apiRes.data);
223
+ }
224
+ } catch {
225
+ }
226
+ }
227
+ }
199
228
  }
200
- );
201
- if (response.code === 200 && response.data) {
202
- this.options.onProgress(response.data);
229
+ } finally {
230
+ reader.releaseLock();
203
231
  }
204
- } catch (error) {
205
- console.warn("\u83B7\u53D6\u4E0A\u4F20\u8FDB\u5EA6\u5931\u8D25:", error);
232
+ }).catch((error) => {
233
+ if (error?.name === "AbortError") return;
234
+ console.warn("\u8FDB\u5EA6\u6D41\u5F0F\u63A5\u53E3\u5F02\u5E38:", error);
235
+ });
236
+ }
237
+ /**
238
+ * 关闭进度流
239
+ */
240
+ closeProgressStream() {
241
+ if (this.progressStreamAbortController) {
242
+ this.progressStreamAbortController.abort();
243
+ this.progressStreamAbortController = null;
206
244
  }
207
245
  }
208
246
  /**
@@ -275,6 +313,7 @@ var ChunkUploader = class {
275
313
  this.options.onComplete(result);
276
314
  return result;
277
315
  }
316
+ this.startProgressStream();
278
317
  console.log("[\u4E0A\u4F20\u6D41\u7A0B] 2. \u51C6\u5907\u5206\u7247");
279
318
  this.prepareChunks();
280
319
  const totalChunks = this.chunks.length;
@@ -318,6 +357,8 @@ var ChunkUploader = class {
318
357
  this.options.onError(err);
319
358
  console.error("[\u4E0A\u4F20\u6D41\u7A0B] \u274C \u4E0A\u4F20\u5931\u8D25:", err);
320
359
  throw err;
360
+ } finally {
361
+ this.closeProgressStream();
321
362
  }
322
363
  }
323
364
  /**
@@ -344,6 +385,7 @@ var ChunkUploader = class {
344
385
  * 取消上传
345
386
  */
346
387
  async cancel() {
388
+ this.closeProgressStream();
347
389
  if (this.taskId && this.status === "uploading" /* UPLOADING */) {
348
390
  try {
349
391
  await this.request(`/api/files/common/cancel/${this.taskId}`, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alibarbar/common",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Alibarbar 通用工具库",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -129,6 +129,28 @@
129
129
  "dist",
130
130
  "README.md"
131
131
  ],
132
+ "scripts": {
133
+ "build": "tsup",
134
+ "build:dev": "SOURCEMAP=true tsup",
135
+ "build:analyze": "tsup --minify false && npm run analyze",
136
+ "build:clean": "rm -rf dist && npm run build",
137
+ "dev": "tsup --watch",
138
+ "test": "jest",
139
+ "test:watch": "jest --watch",
140
+ "test:coverage": "jest --coverage",
141
+ "test:ci": "jest --ci --coverage --maxWorkers=2",
142
+ "lint": "eslint src --ext .ts",
143
+ "lint:fix": "eslint src --ext .ts --fix",
144
+ "format": "prettier --write \"src/**/*.{ts,json,md}\"",
145
+ "format:check": "prettier --check \"src/**/*.{ts,json,md}\"",
146
+ "prepublishOnly": "npm run build && npm run test",
147
+ "prepack": "npm run build",
148
+ "pack:dry-run": "npm pack --dry-run",
149
+ "type-check": "tsc --noEmit",
150
+ "analyze": "echo 'Bundle size analysis - check dist/ directory'",
151
+ "precommit": "npm run lint && npm run format:check && npm run type-check",
152
+ "validate": "npm run lint && npm run format:check && npm run type-check && npm run test"
153
+ },
132
154
  "keywords": [
133
155
  "alibarbar",
134
156
  "common",
@@ -153,6 +175,7 @@
153
175
  "eslint-plugin-prettier": "^5.1.3",
154
176
  "husky": "^9.0.11",
155
177
  "jest": "^29.7.0",
178
+ "jest-environment-jsdom": "^30.2.0",
156
179
  "prettier": "^3.2.4",
157
180
  "qs": "^6.11.2",
158
181
  "ts-jest": "^29.1.2",
@@ -161,30 +184,10 @@
161
184
  },
162
185
  "peerDependencies": {
163
186
  "axios": "^1.7.0",
164
- "qs": "^6.11.2",
165
- "jsencrypt": "^3.5.4"
187
+ "jsencrypt": "^3.5.4",
188
+ "qs": "^6.11.2"
166
189
  },
167
190
  "dependencies": {
168
191
  "jsencrypt": "^3.3.2"
169
- },
170
- "scripts": {
171
- "build": "tsup",
172
- "build:dev": "SOURCEMAP=true tsup",
173
- "build:analyze": "tsup --minify false && npm run analyze",
174
- "build:clean": "rm -rf dist && npm run build",
175
- "dev": "tsup --watch",
176
- "test": "jest",
177
- "test:watch": "jest --watch",
178
- "test:coverage": "jest --coverage",
179
- "test:ci": "jest --ci --coverage --maxWorkers=2",
180
- "lint": "eslint src --ext .ts",
181
- "lint:fix": "eslint src --ext .ts --fix",
182
- "format": "prettier --write \"src/**/*.{ts,json,md}\"",
183
- "format:check": "prettier --check \"src/**/*.{ts,json,md}\"",
184
- "pack:dry-run": "npm pack --dry-run",
185
- "type-check": "tsc --noEmit",
186
- "analyze": "echo 'Bundle size analysis - check dist/ directory'",
187
- "precommit": "npm run lint && npm run format:check && npm run type-check",
188
- "validate": "npm run lint && npm run format:check && npm run type-check && npm run test"
189
192
  }
190
- }
193
+ }