@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.
- package/dist/{index-CqWtZgzO.d.mts → index-j5EqxJaC.d.mts} +9 -3
- package/dist/{index-CqWtZgzO.d.ts → index-j5EqxJaC.d.ts} +9 -3
- package/dist/index.cjs +56 -14
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +56 -14
- package/dist/upload.cjs +56 -14
- package/dist/upload.d.mts +1 -1
- package/dist/upload.d.ts +1 -1
- package/dist/upload.js +56 -14
- package/package.json +27 -24
|
@@ -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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
-
|
|
1296
|
+
startProgressStream() {
|
|
1296
1297
|
if (!this.taskId) return;
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
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
|
-
|
|
1306
|
-
this.options.onProgress(response.data);
|
|
1333
|
+
} finally {
|
|
1334
|
+
reader.releaseLock();
|
|
1307
1335
|
}
|
|
1308
|
-
}
|
|
1309
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
1287
|
+
startProgressStream() {
|
|
1287
1288
|
if (!this.taskId) return;
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
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
|
-
|
|
1297
|
-
this.options.onProgress(response.data);
|
|
1324
|
+
} finally {
|
|
1325
|
+
reader.releaseLock();
|
|
1298
1326
|
}
|
|
1299
|
-
}
|
|
1300
|
-
|
|
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
|
-
|
|
194
|
+
startProgressStream() {
|
|
194
195
|
if (!this.taskId) return;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
204
|
-
this.options.onProgress(response.data);
|
|
231
|
+
} finally {
|
|
232
|
+
reader.releaseLock();
|
|
205
233
|
}
|
|
206
|
-
}
|
|
207
|
-
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
192
|
+
startProgressStream() {
|
|
192
193
|
if (!this.taskId) return;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
202
|
-
this.options.onProgress(response.data);
|
|
229
|
+
} finally {
|
|
230
|
+
reader.releaseLock();
|
|
203
231
|
}
|
|
204
|
-
}
|
|
205
|
-
|
|
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
|
+
"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
|
-
"
|
|
165
|
-
"
|
|
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
|
+
}
|