@alibarbar/common 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +338 -0
- package/dist/algorithm.d.mts +66 -0
- package/dist/algorithm.d.ts +66 -0
- package/dist/algorithm.js +44 -0
- package/dist/algorithm.js.map +1 -0
- package/dist/algorithm.mjs +3 -0
- package/dist/algorithm.mjs.map +1 -0
- package/dist/array.d.mts +139 -0
- package/dist/array.d.ts +139 -0
- package/dist/array.js +84 -0
- package/dist/array.js.map +1 -0
- package/dist/array.mjs +3 -0
- package/dist/array.mjs.map +1 -0
- package/dist/chunk-27UDDVLZ.js +259 -0
- package/dist/chunk-27UDDVLZ.js.map +1 -0
- package/dist/chunk-2FFSQ573.mjs +138 -0
- package/dist/chunk-2FFSQ573.mjs.map +1 -0
- package/dist/chunk-4RGXV4SJ.js +106 -0
- package/dist/chunk-4RGXV4SJ.js.map +1 -0
- package/dist/chunk-56W6YECK.js +374 -0
- package/dist/chunk-56W6YECK.js.map +1 -0
- package/dist/chunk-5BGSUGTI.mjs +128 -0
- package/dist/chunk-5BGSUGTI.mjs.map +1 -0
- package/dist/chunk-7E6GELHJ.mjs +302 -0
- package/dist/chunk-7E6GELHJ.mjs.map +1 -0
- package/dist/chunk-7V5UQXIO.js +89 -0
- package/dist/chunk-7V5UQXIO.js.map +1 -0
- package/dist/chunk-A4SWQXX7.mjs +484 -0
- package/dist/chunk-A4SWQXX7.mjs.map +1 -0
- package/dist/chunk-ALDC6LRJ.mjs +85 -0
- package/dist/chunk-ALDC6LRJ.mjs.map +1 -0
- package/dist/chunk-BHCRFURU.js +491 -0
- package/dist/chunk-BHCRFURU.js.map +1 -0
- package/dist/chunk-D7CS5EKF.js +110 -0
- package/dist/chunk-D7CS5EKF.js.map +1 -0
- package/dist/chunk-DYBSRI7V.js +189 -0
- package/dist/chunk-DYBSRI7V.js.map +1 -0
- package/dist/chunk-F3LAGHPG.js +332 -0
- package/dist/chunk-F3LAGHPG.js.map +1 -0
- package/dist/chunk-HLDFI7R2.mjs +175 -0
- package/dist/chunk-HLDFI7R2.mjs.map +1 -0
- package/dist/chunk-HME2N3VY.mjs +354 -0
- package/dist/chunk-HME2N3VY.mjs.map +1 -0
- package/dist/chunk-I3L42475.js +145 -0
- package/dist/chunk-I3L42475.js.map +1 -0
- package/dist/chunk-JBLX27WD.mjs +240 -0
- package/dist/chunk-JBLX27WD.mjs.map +1 -0
- package/dist/chunk-JHZ7M2MR.mjs +133 -0
- package/dist/chunk-JHZ7M2MR.mjs.map +1 -0
- package/dist/chunk-JK2SE3I2.js +100 -0
- package/dist/chunk-JK2SE3I2.js.map +1 -0
- package/dist/chunk-JQZBPAPO.js +157 -0
- package/dist/chunk-JQZBPAPO.js.map +1 -0
- package/dist/chunk-JXYGC2C5.mjs +100 -0
- package/dist/chunk-JXYGC2C5.mjs.map +1 -0
- package/dist/chunk-KGFTD255.js +104 -0
- package/dist/chunk-KGFTD255.js.map +1 -0
- package/dist/chunk-LBHBNPNJ.mjs +148 -0
- package/dist/chunk-LBHBNPNJ.mjs.map +1 -0
- package/dist/chunk-LCXGZISK.js +158 -0
- package/dist/chunk-LCXGZISK.js.map +1 -0
- package/dist/chunk-LF4CILQS.mjs +87 -0
- package/dist/chunk-LF4CILQS.mjs.map +1 -0
- package/dist/chunk-MMR6XQNX.js +98 -0
- package/dist/chunk-MMR6XQNX.js.map +1 -0
- package/dist/chunk-NSSDYX2U.mjs +80 -0
- package/dist/chunk-NSSDYX2U.mjs.map +1 -0
- package/dist/chunk-O3O67R4I.js +143 -0
- package/dist/chunk-O3O67R4I.js.map +1 -0
- package/dist/chunk-OX5PLOWB.js +90 -0
- package/dist/chunk-OX5PLOWB.js.map +1 -0
- package/dist/chunk-PJ7UCTX4.mjs +362 -0
- package/dist/chunk-PJ7UCTX4.mjs.map +1 -0
- package/dist/chunk-QIBE7GVN.mjs +81 -0
- package/dist/chunk-QIBE7GVN.mjs.map +1 -0
- package/dist/chunk-QIOC54LQ.mjs +130 -0
- package/dist/chunk-QIOC54LQ.mjs.map +1 -0
- package/dist/chunk-QV6MIQ7H.mjs +328 -0
- package/dist/chunk-QV6MIQ7H.mjs.map +1 -0
- package/dist/chunk-TQN37HIN.js +94 -0
- package/dist/chunk-TQN37HIN.js.map +1 -0
- package/dist/chunk-XJTZDXSR.mjs +94 -0
- package/dist/chunk-XJTZDXSR.mjs.map +1 -0
- package/dist/chunk-XVUE53T3.js +361 -0
- package/dist/chunk-XVUE53T3.js.map +1 -0
- package/dist/chunk-Y364QIQH.js +139 -0
- package/dist/chunk-Y364QIQH.js.map +1 -0
- package/dist/chunk-YXM6Q4JS.mjs +94 -0
- package/dist/chunk-YXM6Q4JS.mjs.map +1 -0
- package/dist/chunk-ZDMFMUDR.js +309 -0
- package/dist/chunk-ZDMFMUDR.js.map +1 -0
- package/dist/chunk-ZVJ6NQUM.mjs +82 -0
- package/dist/chunk-ZVJ6NQUM.mjs.map +1 -0
- package/dist/color.d.mts +74 -0
- package/dist/color.d.ts +74 -0
- package/dist/color.js +40 -0
- package/dist/color.js.map +1 -0
- package/dist/color.mjs +3 -0
- package/dist/color.mjs.map +1 -0
- package/dist/crypto.d.mts +92 -0
- package/dist/crypto.d.ts +92 -0
- package/dist/crypto.js +60 -0
- package/dist/crypto.js.map +1 -0
- package/dist/crypto.mjs +3 -0
- package/dist/crypto.mjs.map +1 -0
- package/dist/data-structure.d.mts +213 -0
- package/dist/data-structure.d.ts +213 -0
- package/dist/data-structure.js +32 -0
- package/dist/data-structure.js.map +1 -0
- package/dist/data-structure.mjs +3 -0
- package/dist/data-structure.mjs.map +1 -0
- package/dist/date.d.mts +108 -0
- package/dist/date.d.ts +108 -0
- package/dist/date.js +72 -0
- package/dist/date.js.map +1 -0
- package/dist/date.mjs +3 -0
- package/dist/date.mjs.map +1 -0
- package/dist/dom.d.mts +92 -0
- package/dist/dom.d.ts +92 -0
- package/dist/dom.js +56 -0
- package/dist/dom.js.map +1 -0
- package/dist/dom.mjs +3 -0
- package/dist/dom.mjs.map +1 -0
- package/dist/file.d.mts +44 -0
- package/dist/file.d.ts +44 -0
- package/dist/file.js +32 -0
- package/dist/file.js.map +1 -0
- package/dist/file.mjs +3 -0
- package/dist/file.mjs.map +1 -0
- package/dist/i18n.d.mts +77 -0
- package/dist/i18n.d.ts +77 -0
- package/dist/i18n.js +40 -0
- package/dist/i18n.js.map +1 -0
- package/dist/i18n.mjs +3 -0
- package/dist/i18n.mjs.map +1 -0
- package/dist/index.d.mts +155 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +839 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +22 -0
- package/dist/index.mjs.map +1 -0
- package/dist/network.d.mts +47 -0
- package/dist/network.d.ts +47 -0
- package/dist/network.js +28 -0
- package/dist/network.js.map +1 -0
- package/dist/network.mjs +3 -0
- package/dist/network.mjs.map +1 -0
- package/dist/number.d.mts +100 -0
- package/dist/number.d.ts +100 -0
- package/dist/number.js +56 -0
- package/dist/number.js.map +1 -0
- package/dist/number.mjs +3 -0
- package/dist/number.mjs.map +1 -0
- package/dist/object.d.mts +132 -0
- package/dist/object.d.ts +132 -0
- package/dist/object.js +80 -0
- package/dist/object.js.map +1 -0
- package/dist/object.mjs +3 -0
- package/dist/object.mjs.map +1 -0
- package/dist/performance.d.mts +85 -0
- package/dist/performance.d.ts +85 -0
- package/dist/performance.js +40 -0
- package/dist/performance.js.map +1 -0
- package/dist/performance.mjs +3 -0
- package/dist/performance.mjs.map +1 -0
- package/dist/storage.d.mts +176 -0
- package/dist/storage.d.ts +176 -0
- package/dist/storage.js +33 -0
- package/dist/storage.js.map +1 -0
- package/dist/storage.mjs +4 -0
- package/dist/storage.mjs.map +1 -0
- package/dist/string.d.mts +105 -0
- package/dist/string.d.ts +105 -0
- package/dist/string.js +68 -0
- package/dist/string.js.map +1 -0
- package/dist/string.mjs +3 -0
- package/dist/string.mjs.map +1 -0
- package/dist/tracking.d.mts +182 -0
- package/dist/tracking.d.ts +182 -0
- package/dist/tracking.js +52 -0
- package/dist/tracking.js.map +1 -0
- package/dist/tracking.mjs +3 -0
- package/dist/tracking.mjs.map +1 -0
- package/dist/transform.d.mts +53 -0
- package/dist/transform.d.ts +53 -0
- package/dist/transform.js +32 -0
- package/dist/transform.js.map +1 -0
- package/dist/transform.mjs +3 -0
- package/dist/transform.mjs.map +1 -0
- package/dist/upload-DzlQtUBc.d.mts +202 -0
- package/dist/upload-DzlQtUBc.d.ts +202 -0
- package/dist/upload.d.mts +1 -0
- package/dist/upload.d.ts +1 -0
- package/dist/upload.js +17 -0
- package/dist/upload.js.map +1 -0
- package/dist/upload.mjs +4 -0
- package/dist/upload.mjs.map +1 -0
- package/dist/url.d.mts +82 -0
- package/dist/url.d.ts +82 -0
- package/dist/url.js +44 -0
- package/dist/url.js.map +1 -0
- package/dist/url.mjs +3 -0
- package/dist/url.mjs.map +1 -0
- package/dist/validation.d.mts +83 -0
- package/dist/validation.d.ts +83 -0
- package/dist/validation.js +60 -0
- package/dist/validation.js.map +1 -0
- package/dist/validation.mjs +3 -0
- package/dist/validation.mjs.map +1 -0
- package/package.json +170 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/helper/crypto.ts
|
|
4
|
+
async function sha256(data) {
|
|
5
|
+
const buffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
6
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
|
|
7
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
8
|
+
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
9
|
+
}
|
|
10
|
+
function base64Encode(data) {
|
|
11
|
+
if (typeof data === "string") {
|
|
12
|
+
return btoa(unescape(encodeURIComponent(data)));
|
|
13
|
+
}
|
|
14
|
+
const bytes = new Uint8Array(data);
|
|
15
|
+
let binary = "";
|
|
16
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
17
|
+
binary += String.fromCharCode(bytes[i]);
|
|
18
|
+
}
|
|
19
|
+
return btoa(binary);
|
|
20
|
+
}
|
|
21
|
+
function base64Decode(data) {
|
|
22
|
+
try {
|
|
23
|
+
return decodeURIComponent(escape(atob(data)));
|
|
24
|
+
} catch {
|
|
25
|
+
throw new Error("Invalid Base64 string");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function generateUUID() {
|
|
29
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
30
|
+
return crypto.randomUUID();
|
|
31
|
+
}
|
|
32
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
33
|
+
const r = Math.random() * 16 | 0;
|
|
34
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
35
|
+
return v.toString(16);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function generateRandomString(length, charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") {
|
|
39
|
+
let result = "";
|
|
40
|
+
for (let i = 0; i < length; i++) {
|
|
41
|
+
result += charset.charAt(Math.floor(Math.random() * charset.length));
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
function hash(data) {
|
|
46
|
+
let hashValue = 0;
|
|
47
|
+
for (let i = 0; i < data.length; i++) {
|
|
48
|
+
const char = data.charCodeAt(i);
|
|
49
|
+
hashValue = (hashValue << 5) - hashValue + char;
|
|
50
|
+
hashValue = hashValue & hashValue;
|
|
51
|
+
}
|
|
52
|
+
return Math.abs(hashValue);
|
|
53
|
+
}
|
|
54
|
+
async function generateRSAKeyPair(modulusLength = 2048) {
|
|
55
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
56
|
+
throw new Error("Web Crypto API is not available");
|
|
57
|
+
}
|
|
58
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
59
|
+
{
|
|
60
|
+
name: "RSA-OAEP",
|
|
61
|
+
modulusLength,
|
|
62
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
63
|
+
hash: "SHA-256"
|
|
64
|
+
},
|
|
65
|
+
true,
|
|
66
|
+
["encrypt", "decrypt"]
|
|
67
|
+
);
|
|
68
|
+
return {
|
|
69
|
+
publicKey: keyPair.publicKey,
|
|
70
|
+
privateKey: keyPair.privateKey
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async function rsaEncrypt(data, publicKey) {
|
|
74
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
75
|
+
throw new Error("Web Crypto API is not available");
|
|
76
|
+
}
|
|
77
|
+
const encoder = new TextEncoder();
|
|
78
|
+
const dataBuffer = encoder.encode(data);
|
|
79
|
+
const maxChunkSize = 245;
|
|
80
|
+
const chunks = [];
|
|
81
|
+
for (let i = 0; i < dataBuffer.length; i += maxChunkSize) {
|
|
82
|
+
const chunk = dataBuffer.slice(i, i + maxChunkSize);
|
|
83
|
+
const encrypted = await crypto.subtle.encrypt(
|
|
84
|
+
{
|
|
85
|
+
name: "RSA-OAEP"
|
|
86
|
+
},
|
|
87
|
+
publicKey,
|
|
88
|
+
chunk
|
|
89
|
+
);
|
|
90
|
+
chunks.push(encrypted);
|
|
91
|
+
}
|
|
92
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
93
|
+
const merged = new Uint8Array(totalLength);
|
|
94
|
+
let offset = 0;
|
|
95
|
+
for (const chunk of chunks) {
|
|
96
|
+
merged.set(new Uint8Array(chunk), offset);
|
|
97
|
+
offset += chunk.byteLength;
|
|
98
|
+
}
|
|
99
|
+
return base64Encode(merged.buffer);
|
|
100
|
+
}
|
|
101
|
+
async function rsaDecrypt(encryptedData, privateKey) {
|
|
102
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
103
|
+
throw new Error("Web Crypto API is not available");
|
|
104
|
+
}
|
|
105
|
+
const binaryString = atob(encryptedData);
|
|
106
|
+
const encryptedArray = new Uint8Array(binaryString.length);
|
|
107
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
108
|
+
encryptedArray[i] = binaryString.charCodeAt(i);
|
|
109
|
+
}
|
|
110
|
+
const chunkSize = 256;
|
|
111
|
+
const chunks = [];
|
|
112
|
+
for (let i = 0; i < encryptedArray.length; i += chunkSize) {
|
|
113
|
+
const chunk = encryptedArray.slice(i, i + chunkSize);
|
|
114
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
115
|
+
{
|
|
116
|
+
name: "RSA-OAEP"
|
|
117
|
+
},
|
|
118
|
+
privateKey,
|
|
119
|
+
chunk
|
|
120
|
+
);
|
|
121
|
+
const decoder = new TextDecoder();
|
|
122
|
+
chunks.push(decoder.decode(decrypted));
|
|
123
|
+
}
|
|
124
|
+
return chunks.join("");
|
|
125
|
+
}
|
|
126
|
+
async function exportPublicKey(publicKey) {
|
|
127
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
128
|
+
throw new Error("Web Crypto API is not available");
|
|
129
|
+
}
|
|
130
|
+
const exported = await crypto.subtle.exportKey("spki", publicKey);
|
|
131
|
+
return base64Encode(exported);
|
|
132
|
+
}
|
|
133
|
+
async function exportPrivateKey(privateKey) {
|
|
134
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
135
|
+
throw new Error("Web Crypto API is not available");
|
|
136
|
+
}
|
|
137
|
+
const exported = await crypto.subtle.exportKey("pkcs8", privateKey);
|
|
138
|
+
return base64Encode(exported);
|
|
139
|
+
}
|
|
140
|
+
async function importPublicKey(keyData) {
|
|
141
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
142
|
+
throw new Error("Web Crypto API is not available");
|
|
143
|
+
}
|
|
144
|
+
const keyBuffer = base64Decode(keyData);
|
|
145
|
+
const keyArray = new Uint8Array(keyBuffer.split("").map((char) => char.charCodeAt(0)));
|
|
146
|
+
return crypto.subtle.importKey(
|
|
147
|
+
"spki",
|
|
148
|
+
keyArray.buffer,
|
|
149
|
+
{
|
|
150
|
+
name: "RSA-OAEP",
|
|
151
|
+
hash: "SHA-256"
|
|
152
|
+
},
|
|
153
|
+
true,
|
|
154
|
+
["encrypt"]
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
async function importPrivateKey(keyData) {
|
|
158
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
159
|
+
throw new Error("Web Crypto API is not available");
|
|
160
|
+
}
|
|
161
|
+
const keyBuffer = base64Decode(keyData);
|
|
162
|
+
const keyArray = new Uint8Array(keyBuffer.split("").map((char) => char.charCodeAt(0)));
|
|
163
|
+
return crypto.subtle.importKey(
|
|
164
|
+
"pkcs8",
|
|
165
|
+
keyArray.buffer,
|
|
166
|
+
{
|
|
167
|
+
name: "RSA-OAEP",
|
|
168
|
+
hash: "SHA-256"
|
|
169
|
+
},
|
|
170
|
+
true,
|
|
171
|
+
["decrypt"]
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
exports.base64Decode = base64Decode;
|
|
176
|
+
exports.base64Encode = base64Encode;
|
|
177
|
+
exports.exportPrivateKey = exportPrivateKey;
|
|
178
|
+
exports.exportPublicKey = exportPublicKey;
|
|
179
|
+
exports.generateRSAKeyPair = generateRSAKeyPair;
|
|
180
|
+
exports.generateRandomString = generateRandomString;
|
|
181
|
+
exports.generateUUID = generateUUID;
|
|
182
|
+
exports.hash = hash;
|
|
183
|
+
exports.importPrivateKey = importPrivateKey;
|
|
184
|
+
exports.importPublicKey = importPublicKey;
|
|
185
|
+
exports.rsaDecrypt = rsaDecrypt;
|
|
186
|
+
exports.rsaEncrypt = rsaEncrypt;
|
|
187
|
+
exports.sha256 = sha256;
|
|
188
|
+
//# sourceMappingURL=chunk-DYBSRI7V.js.map
|
|
189
|
+
//# sourceMappingURL=chunk-DYBSRI7V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/helper/crypto.ts"],"names":[],"mappings":"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","file":"chunk-DYBSRI7V.js","sourcesContent":["/**\n * 加密工具函数\n */\n\n/**\n * SHA256加密\n * @param data - 要加密的数据(字符串或ArrayBuffer)\n * @returns Promise<string> 加密后的十六进制字符串\n */\nexport async function sha256(data: string | ArrayBuffer): Promise<string> {\n const buffer = typeof data === 'string' ? new TextEncoder().encode(data) : data;\n const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Base64编码\n * @param data - 要编码的数据(字符串或ArrayBuffer)\n * @returns Base64编码字符串\n */\nexport function base64Encode(data: string | ArrayBuffer): string {\n if (typeof data === 'string') {\n return btoa(unescape(encodeURIComponent(data)));\n }\n const bytes = new Uint8Array(data);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/**\n * Base64解码\n * @param data - Base64编码字符串\n * @returns 解码后的字符串\n */\nexport function base64Decode(data: string): string {\n try {\n return decodeURIComponent(escape(atob(data)));\n } catch {\n throw new Error('Invalid Base64 string');\n }\n}\n\n/**\n * 生成UUID v4\n * @returns UUID字符串\n */\nexport function generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n // 降级方案\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * 生成随机字符串\n * @param length - 字符串长度\n * @param charset - 字符集,默认为 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\n * @returns 随机字符串\n */\nexport function generateRandomString(\n length: number,\n charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\n): string {\n let result = '';\n for (let i = 0; i < length; i++) {\n result += charset.charAt(Math.floor(Math.random() * charset.length));\n }\n return result;\n}\n\n/**\n * 通用哈希函数(简单实现)\n * @param data - 要哈希的数据\n * @returns 哈希值\n */\nexport function hash(data: string): number {\n let hashValue = 0;\n for (let i = 0; i < data.length; i++) {\n const char = data.charCodeAt(i);\n hashValue = (hashValue << 5) - hashValue + char;\n hashValue = hashValue & hashValue; // 转换为32位整数\n }\n return Math.abs(hashValue);\n}\n\n/**\n * RSA密钥对类型\n */\nexport interface RSAKeyPair {\n publicKey: CryptoKey;\n privateKey: CryptoKey;\n}\n\n/**\n * 生成RSA密钥对\n * @param modulusLength - 密钥长度,默认为 2048\n * @returns Promise<RSAKeyPair> RSA密钥对\n */\nexport async function generateRSAKeyPair(modulusLength = 2048): Promise<RSAKeyPair> {\n if (typeof crypto === 'undefined' || !crypto.subtle) {\n throw new Error('Web Crypto API is not available');\n }\n\n const keyPair = await crypto.subtle.generateKey(\n {\n name: 'RSA-OAEP',\n modulusLength,\n publicExponent: new Uint8Array([1, 0, 1]),\n hash: 'SHA-256',\n },\n true,\n ['encrypt', 'decrypt']\n );\n\n return {\n publicKey: keyPair.publicKey,\n privateKey: keyPair.privateKey,\n };\n}\n\n/**\n * RSA加密\n * @param data - 要加密的数据(字符串)\n * @param publicKey - 公钥\n * @returns Promise<string> Base64编码的加密数据\n */\nexport async function rsaEncrypt(data: string, publicKey: CryptoKey): Promise<string> {\n if (typeof crypto === 'undefined' || !crypto.subtle) {\n throw new Error('Web Crypto API is not available');\n }\n\n const encoder = new TextEncoder();\n const dataBuffer = encoder.encode(data);\n\n // RSA-OAEP 最大加密长度受密钥长度限制\n // 2048位密钥最多加密245字节,需要分块处理\n const maxChunkSize = 245;\n const chunks: ArrayBuffer[] = [];\n\n for (let i = 0; i < dataBuffer.length; i += maxChunkSize) {\n const chunk = dataBuffer.slice(i, i + maxChunkSize);\n const encrypted = await crypto.subtle.encrypt(\n {\n name: 'RSA-OAEP',\n },\n publicKey,\n chunk\n );\n chunks.push(encrypted);\n }\n\n // 将所有加密块合并并转换为Base64\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);\n const merged = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n merged.set(new Uint8Array(chunk), offset);\n offset += chunk.byteLength;\n }\n\n return base64Encode(merged.buffer);\n}\n\n/**\n * RSA解密\n * @param encryptedData - Base64编码的加密数据\n * @param privateKey - 私钥\n * @returns Promise<string> 解密后的字符串\n */\nexport async function rsaDecrypt(encryptedData: string, privateKey: CryptoKey): Promise<string> {\n if (typeof crypto === 'undefined' || !crypto.subtle) {\n throw new Error('Web Crypto API is not available');\n }\n\n // Base64解码为ArrayBuffer\n const binaryString = atob(encryptedData);\n const encryptedArray = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n encryptedArray[i] = binaryString.charCodeAt(i);\n }\n\n // RSA-OAEP 解密块大小(256字节对于2048位密钥)\n const chunkSize = 256;\n const chunks: string[] = [];\n\n for (let i = 0; i < encryptedArray.length; i += chunkSize) {\n const chunk = encryptedArray.slice(i, i + chunkSize);\n const decrypted = await crypto.subtle.decrypt(\n {\n name: 'RSA-OAEP',\n },\n privateKey,\n chunk\n );\n const decoder = new TextDecoder();\n chunks.push(decoder.decode(decrypted));\n }\n\n return chunks.join('');\n}\n\n/**\n * 导出公钥为Base64字符串\n * @param publicKey - 公钥\n * @returns Promise<string> Base64编码的公钥\n */\nexport async function exportPublicKey(publicKey: CryptoKey): Promise<string> {\n if (typeof crypto === 'undefined' || !crypto.subtle) {\n throw new Error('Web Crypto API is not available');\n }\n\n const exported = await crypto.subtle.exportKey('spki', publicKey);\n return base64Encode(exported);\n}\n\n/**\n * 导出私钥为Base64字符串\n * @param privateKey - 私钥\n * @returns Promise<string> Base64编码的私钥\n */\nexport async function exportPrivateKey(privateKey: CryptoKey): Promise<string> {\n if (typeof crypto === 'undefined' || !crypto.subtle) {\n throw new Error('Web Crypto API is not available');\n }\n\n const exported = await crypto.subtle.exportKey('pkcs8', privateKey);\n return base64Encode(exported);\n}\n\n/**\n * 从Base64字符串导入公钥\n * @param keyData - Base64编码的公钥\n * @returns Promise<CryptoKey> 公钥对象\n */\nexport async function importPublicKey(keyData: string): Promise<CryptoKey> {\n if (typeof crypto === 'undefined' || !crypto.subtle) {\n throw new Error('Web Crypto API is not available');\n }\n\n const keyBuffer = base64Decode(keyData);\n const keyArray = new Uint8Array(keyBuffer.split('').map(char => char.charCodeAt(0)));\n\n return crypto.subtle.importKey(\n 'spki',\n keyArray.buffer,\n {\n name: 'RSA-OAEP',\n hash: 'SHA-256',\n },\n true,\n ['encrypt']\n );\n}\n\n/**\n * 从Base64字符串导入私钥\n * @param keyData - Base64编码的私钥\n * @returns Promise<CryptoKey> 私钥对象\n */\nexport async function importPrivateKey(keyData: string): Promise<CryptoKey> {\n if (typeof crypto === 'undefined' || !crypto.subtle) {\n throw new Error('Web Crypto API is not available');\n }\n\n const keyBuffer = base64Decode(keyData);\n const keyArray = new Uint8Array(keyBuffer.split('').map(char => char.charCodeAt(0)));\n\n return crypto.subtle.importKey(\n 'pkcs8',\n keyArray.buffer,\n {\n name: 'RSA-OAEP',\n hash: 'SHA-256',\n },\n true,\n ['decrypt']\n );\n}\n"]}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunk7V5UQXIO_js = require('./chunk-7V5UQXIO.js');
|
|
4
|
+
|
|
5
|
+
// src/types/upload.ts
|
|
6
|
+
var UploadStatus = /* @__PURE__ */ ((UploadStatus2) => {
|
|
7
|
+
UploadStatus2["PENDING"] = "pending";
|
|
8
|
+
UploadStatus2["UPLOADING"] = "uploading";
|
|
9
|
+
UploadStatus2["PAUSED"] = "paused";
|
|
10
|
+
UploadStatus2["COMPLETED"] = "completed";
|
|
11
|
+
UploadStatus2["FAILED"] = "failed";
|
|
12
|
+
UploadStatus2["CANCELLED"] = "cancelled";
|
|
13
|
+
return UploadStatus2;
|
|
14
|
+
})(UploadStatus || {});
|
|
15
|
+
|
|
16
|
+
// src/browser/upload.ts
|
|
17
|
+
var ChunkUploader = class {
|
|
18
|
+
constructor(file, options = {}) {
|
|
19
|
+
this.taskId = null;
|
|
20
|
+
this.chunks = [];
|
|
21
|
+
this.uploadedChunks = /* @__PURE__ */ new Set();
|
|
22
|
+
this.status = "pending" /* PENDING */;
|
|
23
|
+
this.abortController = null;
|
|
24
|
+
this.file = file;
|
|
25
|
+
this.options = {
|
|
26
|
+
chunkSize: options.chunkSize || 2 * 1024 * 1024,
|
|
27
|
+
// 默认2MB
|
|
28
|
+
concurrency: options.concurrency || 3,
|
|
29
|
+
retryCount: options.retryCount || 3,
|
|
30
|
+
retryDelay: options.retryDelay || 1e3,
|
|
31
|
+
baseURL: options.baseURL || "",
|
|
32
|
+
headers: options.headers || {},
|
|
33
|
+
onProgress: options.onProgress || (() => {
|
|
34
|
+
}),
|
|
35
|
+
onComplete: options.onComplete || (() => {
|
|
36
|
+
}),
|
|
37
|
+
onError: options.onError || (() => {
|
|
38
|
+
})
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 初始化上传
|
|
43
|
+
*/
|
|
44
|
+
async initUpload() {
|
|
45
|
+
const fileMd5 = await this.calculateFileMD5();
|
|
46
|
+
const request = {
|
|
47
|
+
fileName: this.file.name,
|
|
48
|
+
fileSize: this.file.size,
|
|
49
|
+
fileMd5,
|
|
50
|
+
chunkSize: this.options.chunkSize
|
|
51
|
+
};
|
|
52
|
+
const response = await this.request("/api/files/common/init", {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: {
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
...this.options.headers
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify(request)
|
|
59
|
+
});
|
|
60
|
+
if (response.code !== 200) {
|
|
61
|
+
throw new Error(response.message || "\u521D\u59CB\u5316\u4E0A\u4F20\u5931\u8D25");
|
|
62
|
+
}
|
|
63
|
+
return response.data;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 计算文件MD5(简化版,实际应该使用spark-md5等库)
|
|
67
|
+
*/
|
|
68
|
+
async calculateFileMD5() {
|
|
69
|
+
const chunks = chunk7V5UQXIO_js.splitFileIntoChunks(this.file, this.options.chunkSize);
|
|
70
|
+
const md5Promises = chunks.map(
|
|
71
|
+
(chunk, index) => chunk7V5UQXIO_js.calculateBlobMD5(chunk).then((md5) => ({ index, md5 }))
|
|
72
|
+
);
|
|
73
|
+
const md5Results = await Promise.all(md5Promises);
|
|
74
|
+
return md5Results.map((r) => r.md5).join("");
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 准备分片
|
|
78
|
+
*/
|
|
79
|
+
prepareChunks() {
|
|
80
|
+
const blobChunks = chunk7V5UQXIO_js.splitFileIntoChunks(this.file, this.options.chunkSize);
|
|
81
|
+
this.chunks = blobChunks.map((blob, index) => ({
|
|
82
|
+
index,
|
|
83
|
+
start: index * this.options.chunkSize,
|
|
84
|
+
end: Math.min((index + 1) * this.options.chunkSize, this.file.size),
|
|
85
|
+
blob
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 获取已上传的分片列表
|
|
90
|
+
*/
|
|
91
|
+
async getUploadedChunks() {
|
|
92
|
+
if (!this.taskId) return [];
|
|
93
|
+
try {
|
|
94
|
+
const response = await this.request(
|
|
95
|
+
`/api/files/common/chunks/${this.taskId}`,
|
|
96
|
+
{
|
|
97
|
+
method: "GET",
|
|
98
|
+
headers: this.options.headers
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
if (response.code === 200) {
|
|
102
|
+
return response.data || [];
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.warn("\u83B7\u53D6\u5DF2\u4E0A\u4F20\u5206\u7247\u5931\u8D25:", error);
|
|
106
|
+
}
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 上传单个分片
|
|
111
|
+
*/
|
|
112
|
+
async uploadChunk(chunkInfo, retryCount = 0) {
|
|
113
|
+
if (!this.taskId) {
|
|
114
|
+
throw new Error("\u4EFB\u52A1ID\u4E0D\u5B58\u5728");
|
|
115
|
+
}
|
|
116
|
+
if (this.uploadedChunks.has(chunkInfo.index)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const chunkMd5 = await chunk7V5UQXIO_js.calculateBlobMD5(chunkInfo.blob);
|
|
121
|
+
const formData = new FormData();
|
|
122
|
+
formData.append("file", chunkInfo.blob, this.file.name);
|
|
123
|
+
const url = `/api/files/common/chunk?taskId=${this.taskId}&chunkIndex=${chunkInfo.index}&chunkMd5=${chunkMd5}`;
|
|
124
|
+
const response = await this.request(url, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers: this.options.headers,
|
|
127
|
+
body: formData
|
|
128
|
+
});
|
|
129
|
+
if (response.code === 200 && response.data.success) {
|
|
130
|
+
this.uploadedChunks.add(chunkInfo.index);
|
|
131
|
+
this.updateProgress();
|
|
132
|
+
} else {
|
|
133
|
+
throw new Error(response.message || "\u5206\u7247\u4E0A\u4F20\u5931\u8D25");
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (retryCount < this.options.retryCount) {
|
|
137
|
+
await this.delay(this.options.retryDelay);
|
|
138
|
+
return this.uploadChunk(chunkInfo, retryCount + 1);
|
|
139
|
+
}
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 并发上传分片
|
|
145
|
+
*/
|
|
146
|
+
async uploadChunksConcurrently() {
|
|
147
|
+
const chunksToUpload = this.chunks.filter((chunk) => !this.uploadedChunks.has(chunk.index));
|
|
148
|
+
const uploadPromises = [];
|
|
149
|
+
let currentIndex = 0;
|
|
150
|
+
const uploadNext = async () => {
|
|
151
|
+
while (currentIndex < chunksToUpload.length && this.status === "uploading" /* UPLOADING */) {
|
|
152
|
+
const chunk = chunksToUpload[currentIndex++];
|
|
153
|
+
const promise = this.uploadChunk(chunk).then(() => {
|
|
154
|
+
if (currentIndex < chunksToUpload.length) {
|
|
155
|
+
return uploadNext();
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
uploadPromises.push(promise);
|
|
159
|
+
if (uploadPromises.length >= this.options.concurrency) {
|
|
160
|
+
await Promise.race(uploadPromises);
|
|
161
|
+
uploadPromises.splice(
|
|
162
|
+
uploadPromises.findIndex((p) => p === promise),
|
|
163
|
+
1
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
const concurrencyPromises = [];
|
|
169
|
+
for (let i = 0; i < this.options.concurrency; i++) {
|
|
170
|
+
concurrencyPromises.push(uploadNext());
|
|
171
|
+
}
|
|
172
|
+
await Promise.all(concurrencyPromises);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 完成上传
|
|
176
|
+
*/
|
|
177
|
+
async completeUpload() {
|
|
178
|
+
if (!this.taskId) {
|
|
179
|
+
throw new Error("\u4EFB\u52A1ID\u4E0D\u5B58\u5728");
|
|
180
|
+
}
|
|
181
|
+
const response = await this.request(
|
|
182
|
+
`/api/files/common/complete/${this.taskId}`,
|
|
183
|
+
{
|
|
184
|
+
method: "POST",
|
|
185
|
+
headers: this.options.headers
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
if (response.code !== 200) {
|
|
189
|
+
throw new Error(response.message || "\u5B8C\u6210\u4E0A\u4F20\u5931\u8D25");
|
|
190
|
+
}
|
|
191
|
+
return response.data;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 更新上传进度
|
|
195
|
+
*/
|
|
196
|
+
async updateProgress() {
|
|
197
|
+
if (!this.taskId) return;
|
|
198
|
+
try {
|
|
199
|
+
const response = await this.request(
|
|
200
|
+
`/api/files/common/progress/${this.taskId}`,
|
|
201
|
+
{
|
|
202
|
+
method: "GET",
|
|
203
|
+
headers: this.options.headers
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
if (response.code === 200 && this.options.onProgress) {
|
|
207
|
+
this.options.onProgress(response.data);
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.warn("\u83B7\u53D6\u4E0A\u4F20\u8FDB\u5EA6\u5931\u8D25:", error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* 开始上传
|
|
215
|
+
*/
|
|
216
|
+
async upload() {
|
|
217
|
+
try {
|
|
218
|
+
this.status = "uploading" /* UPLOADING */;
|
|
219
|
+
this.abortController = new AbortController();
|
|
220
|
+
const initResponse = await this.initUpload();
|
|
221
|
+
this.taskId = initResponse.taskId;
|
|
222
|
+
if (initResponse.instantUpload && initResponse.fileUrl) {
|
|
223
|
+
this.status = "completed" /* COMPLETED */;
|
|
224
|
+
const result2 = {
|
|
225
|
+
taskId: this.taskId,
|
|
226
|
+
fileUrl: initResponse.fileUrl,
|
|
227
|
+
fileName: this.file.name,
|
|
228
|
+
fileSize: this.file.size,
|
|
229
|
+
fileMd5: "",
|
|
230
|
+
success: true,
|
|
231
|
+
message: "\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u79D2\u4F20\u6210\u529F"
|
|
232
|
+
};
|
|
233
|
+
this.options.onComplete(result2);
|
|
234
|
+
return result2;
|
|
235
|
+
}
|
|
236
|
+
this.prepareChunks();
|
|
237
|
+
const existingChunks = await this.getUploadedChunks();
|
|
238
|
+
existingChunks.forEach((index) => this.uploadedChunks.add(index));
|
|
239
|
+
await this.uploadChunksConcurrently();
|
|
240
|
+
const result = await this.completeUpload();
|
|
241
|
+
this.status = "completed" /* COMPLETED */;
|
|
242
|
+
this.options.onComplete(result);
|
|
243
|
+
return result;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
this.status = "failed" /* FAILED */;
|
|
246
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
247
|
+
this.options.onError(err);
|
|
248
|
+
throw err;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* 暂停上传
|
|
253
|
+
*/
|
|
254
|
+
pause() {
|
|
255
|
+
if (this.status === "uploading" /* UPLOADING */) {
|
|
256
|
+
this.status = "paused" /* PAUSED */;
|
|
257
|
+
if (this.abortController) {
|
|
258
|
+
this.abortController.abort();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* 恢复上传
|
|
264
|
+
*/
|
|
265
|
+
async resume() {
|
|
266
|
+
if (this.status === "paused" /* PAUSED */) {
|
|
267
|
+
return this.upload();
|
|
268
|
+
}
|
|
269
|
+
throw new Error("\u5F53\u524D\u72B6\u6001\u65E0\u6CD5\u6062\u590D\u4E0A\u4F20");
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* 取消上传
|
|
273
|
+
*/
|
|
274
|
+
async cancel() {
|
|
275
|
+
if (this.taskId && this.status === "uploading" /* UPLOADING */) {
|
|
276
|
+
try {
|
|
277
|
+
await this.request(`/api/files/common/cancel/${this.taskId}`, {
|
|
278
|
+
method: "POST",
|
|
279
|
+
headers: this.options.headers
|
|
280
|
+
});
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.warn("\u53D6\u6D88\u4E0A\u4F20\u5931\u8D25:", error);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
this.status = "cancelled" /* CANCELLED */;
|
|
286
|
+
if (this.abortController) {
|
|
287
|
+
this.abortController.abort();
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* 获取当前状态
|
|
292
|
+
*/
|
|
293
|
+
getStatus() {
|
|
294
|
+
return this.status;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* 获取任务ID
|
|
298
|
+
*/
|
|
299
|
+
getTaskId() {
|
|
300
|
+
return this.taskId;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* HTTP请求封装
|
|
304
|
+
*/
|
|
305
|
+
async request(url, options = {}) {
|
|
306
|
+
const fullUrl = `${this.options.baseURL}${url}`;
|
|
307
|
+
const signal = this.abortController?.signal;
|
|
308
|
+
const response = await fetch(fullUrl, {
|
|
309
|
+
...options,
|
|
310
|
+
signal
|
|
311
|
+
});
|
|
312
|
+
if (!response.ok) {
|
|
313
|
+
throw new Error(`HTTP\u9519\u8BEF: ${response.status} ${response.statusText}`);
|
|
314
|
+
}
|
|
315
|
+
return response.json();
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* 延迟函数
|
|
319
|
+
*/
|
|
320
|
+
delay(ms) {
|
|
321
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
function createUploader(file, options) {
|
|
325
|
+
return new ChunkUploader(file, options);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
exports.ChunkUploader = ChunkUploader;
|
|
329
|
+
exports.UploadStatus = UploadStatus;
|
|
330
|
+
exports.createUploader = createUploader;
|
|
331
|
+
//# sourceMappingURL=chunk-F3LAGHPG.js.map
|
|
332
|
+
//# sourceMappingURL=chunk-F3LAGHPG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types/upload.ts","../src/browser/upload.ts"],"names":["UploadStatus","splitFileIntoChunks","calculateBlobMD5","result"],"mappings":"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","file":"chunk-F3LAGHPG.js","sourcesContent":["/**\n * 文件上传相关类型定义\n */\n\n/**\n * 上传初始化请求参数\n */\nexport interface UploadInitRequest {\n fileName: string;\n fileSize: number;\n fileMd5: string;\n chunkSize?: number;\n}\n\n/**\n * 上传初始化响应\n */\nexport interface UploadInitResponse {\n taskId: string;\n totalChunks: number;\n existingChunks: number[];\n instantUpload: boolean;\n fileUrl?: string;\n chunkSize: number;\n}\n\n/**\n * 分片上传响应\n */\nexport interface ChunkUploadResponse {\n chunkIndex: number;\n success: boolean;\n message: string;\n chunkMd5: string;\n}\n\n/**\n * 完成上传响应\n */\nexport interface CompleteUploadResponse {\n taskId: string;\n fileUrl: string;\n fileName: string;\n fileSize: number;\n fileMd5: string;\n success: boolean;\n message: string;\n}\n\n/**\n * 上传进度信息\n */\nexport interface UploadProgress {\n taskId: string;\n fileName: string;\n fileSize: number;\n totalChunks: number;\n uploadedChunks: number;\n percentage: number;\n status: string;\n uploadedSize: number;\n}\n\n/**\n * API响应包装类型\n */\nexport interface ApiResponse<T> {\n code: number;\n message: string;\n data: T;\n timestamp: number;\n}\n\n/**\n * 上传配置选项\n */\nexport interface UploadOptions {\n /** 分片大小(字节),默认 2MB */\n chunkSize?: number;\n /** 并发上传数量,默认 3 */\n concurrency?: number;\n /** 重试次数,默认 3 */\n retryCount?: number;\n /** 重试延迟(毫秒),默认 1000 */\n retryDelay?: number;\n /** 基础API地址 */\n baseURL?: string;\n /** 请求头 */\n headers?: Record<string, string>;\n /** 上传进度回调 */\n onProgress?: (progress: UploadProgress) => void;\n /** 上传完成回调 */\n onComplete?: (result: CompleteUploadResponse) => void;\n /** 上传错误回调 */\n onError?: (error: Error) => void;\n}\n\n/**\n * 上传状态\n */\nexport enum UploadStatus {\n PENDING = 'pending',\n UPLOADING = 'uploading',\n PAUSED = 'paused',\n COMPLETED = 'completed',\n FAILED = 'failed',\n CANCELLED = 'cancelled',\n}\n\n/**\n * 文件分片信息\n */\nexport interface ChunkInfo {\n index: number;\n start: number;\n end: number;\n blob: Blob;\n md5?: string;\n}\n","/**\n * 文件分片上传工具类\n * 支持分片上传、断点续传、进度追踪\n */\n\nimport {\n UploadOptions,\n UploadInitRequest,\n UploadInitResponse,\n ChunkUploadResponse,\n CompleteUploadResponse,\n UploadProgress,\n UploadStatus,\n ChunkInfo,\n ApiResponse,\n} from '../types/upload';\nimport { calculateBlobMD5, splitFileIntoChunks } from './file';\n\n/**\n * 文件上传器类\n */\nexport class ChunkUploader {\n private file: File;\n private options: Required<UploadOptions>;\n private taskId: string | null = null;\n private chunks: ChunkInfo[] = [];\n private uploadedChunks: Set<number> = new Set();\n private status: UploadStatus = UploadStatus.PENDING;\n private abortController: AbortController | null = null;\n\n constructor(file: File, options: UploadOptions = {}) {\n this.file = file;\n this.options = {\n chunkSize: options.chunkSize || 2 * 1024 * 1024, // 默认2MB\n concurrency: options.concurrency || 3,\n retryCount: options.retryCount || 3,\n retryDelay: options.retryDelay || 1000,\n baseURL: options.baseURL || '',\n headers: options.headers || {},\n onProgress: options.onProgress || (() => {}),\n onComplete: options.onComplete || (() => {}),\n onError: options.onError || (() => {}),\n };\n }\n\n /**\n * 初始化上传\n */\n private async initUpload(): Promise<UploadInitResponse> {\n const fileMd5 = await this.calculateFileMD5();\n const request: UploadInitRequest = {\n fileName: this.file.name,\n fileSize: this.file.size,\n fileMd5,\n chunkSize: this.options.chunkSize,\n };\n\n const response = await this.request<ApiResponse<UploadInitResponse>>('/api/files/common/init', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.headers,\n },\n body: JSON.stringify(request),\n });\n\n if (response.code !== 200) {\n throw new Error(response.message || '初始化上传失败');\n }\n\n return response.data;\n }\n\n /**\n * 计算文件MD5(简化版,实际应该使用spark-md5等库)\n */\n private async calculateFileMD5(): Promise<string> {\n // 这里使用简化的方法,实际项目中应该使用spark-md5库\n const chunks = splitFileIntoChunks(this.file, this.options.chunkSize);\n const md5Promises = chunks.map((chunk, index) =>\n calculateBlobMD5(chunk).then(md5 => ({ index, md5 }))\n );\n const md5Results = await Promise.all(md5Promises);\n // 简单拼接,实际应该使用正确的MD5算法\n return md5Results.map(r => r.md5).join('');\n }\n\n /**\n * 准备分片\n */\n private prepareChunks(): void {\n const blobChunks = splitFileIntoChunks(this.file, this.options.chunkSize);\n this.chunks = blobChunks.map((blob, index) => ({\n index,\n start: index * this.options.chunkSize,\n end: Math.min((index + 1) * this.options.chunkSize, this.file.size),\n blob,\n }));\n }\n\n /**\n * 获取已上传的分片列表\n */\n private async getUploadedChunks(): Promise<number[]> {\n if (!this.taskId) return [];\n\n try {\n const response = await this.request<ApiResponse<number[]>>(\n `/api/files/common/chunks/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200) {\n return response.data || [];\n }\n } catch (error) {\n console.warn('获取已上传分片失败:', error);\n }\n\n return [];\n }\n\n /**\n * 上传单个分片\n */\n private async uploadChunk(chunkInfo: ChunkInfo, retryCount = 0): Promise<void> {\n if (!this.taskId) {\n throw new Error('任务ID不存在');\n }\n\n if (this.uploadedChunks.has(chunkInfo.index)) {\n return; // 已上传,跳过\n }\n\n try {\n // 计算分片MD5\n const chunkMd5 = await calculateBlobMD5(chunkInfo.blob);\n\n const formData = new FormData();\n formData.append('file', chunkInfo.blob, this.file.name);\n\n const url = `/api/files/common/chunk?taskId=${this.taskId}&chunkIndex=${chunkInfo.index}&chunkMd5=${chunkMd5}`;\n\n const response = await this.request<ApiResponse<ChunkUploadResponse>>(url, {\n method: 'POST',\n headers: this.options.headers,\n body: formData,\n });\n\n if (response.code === 200 && response.data.success) {\n this.uploadedChunks.add(chunkInfo.index);\n this.updateProgress();\n } else {\n throw new Error(response.message || '分片上传失败');\n }\n } catch (error) {\n if (retryCount < this.options.retryCount) {\n await this.delay(this.options.retryDelay);\n return this.uploadChunk(chunkInfo, retryCount + 1);\n }\n throw error;\n }\n }\n\n /**\n * 并发上传分片\n */\n private async uploadChunksConcurrently(): Promise<void> {\n const chunksToUpload = this.chunks.filter(chunk => !this.uploadedChunks.has(chunk.index));\n\n const uploadPromises: Promise<void>[] = [];\n let currentIndex = 0;\n\n const uploadNext = async (): Promise<void> => {\n while (currentIndex < chunksToUpload.length && this.status === UploadStatus.UPLOADING) {\n const chunk = chunksToUpload[currentIndex++];\n const promise = this.uploadChunk(chunk).then(() => {\n if (currentIndex < chunksToUpload.length) {\n return uploadNext();\n }\n });\n uploadPromises.push(promise);\n\n if (uploadPromises.length >= this.options.concurrency) {\n await Promise.race(uploadPromises);\n uploadPromises.splice(\n uploadPromises.findIndex(p => p === promise),\n 1\n );\n }\n }\n };\n\n // 启动并发上传\n const concurrencyPromises: Promise<void>[] = [];\n for (let i = 0; i < this.options.concurrency; i++) {\n concurrencyPromises.push(uploadNext());\n }\n\n await Promise.all(concurrencyPromises);\n }\n\n /**\n * 完成上传\n */\n private async completeUpload(): Promise<CompleteUploadResponse> {\n if (!this.taskId) {\n throw new Error('任务ID不存在');\n }\n\n const response = await this.request<ApiResponse<CompleteUploadResponse>>(\n `/api/files/common/complete/${this.taskId}`,\n {\n method: 'POST',\n headers: this.options.headers,\n }\n );\n\n if (response.code !== 200) {\n throw new Error(response.message || '完成上传失败');\n }\n\n return response.data;\n }\n\n /**\n * 更新上传进度\n */\n private async updateProgress(): Promise<void> {\n if (!this.taskId) return;\n\n try {\n const response = await this.request<ApiResponse<UploadProgress>>(\n `/api/files/common/progress/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200 && this.options.onProgress) {\n this.options.onProgress(response.data);\n }\n } catch (error) {\n console.warn('获取上传进度失败:', error);\n }\n }\n\n /**\n * 开始上传\n */\n async upload(): Promise<CompleteUploadResponse> {\n try {\n this.status = UploadStatus.UPLOADING;\n this.abortController = new AbortController();\n\n // 1. 初始化上传\n const initResponse = await this.initUpload();\n this.taskId = initResponse.taskId;\n\n // 如果已经完成上传(秒传)\n if (initResponse.instantUpload && initResponse.fileUrl) {\n this.status = UploadStatus.COMPLETED;\n const result: CompleteUploadResponse = {\n taskId: this.taskId,\n fileUrl: initResponse.fileUrl,\n fileName: this.file.name,\n fileSize: this.file.size,\n fileMd5: '',\n success: true,\n message: '文件已存在,秒传成功',\n };\n this.options.onComplete(result);\n return result;\n }\n\n // 2. 准备分片\n this.prepareChunks();\n\n // 3. 获取已上传的分片(断点续传)\n const existingChunks = await this.getUploadedChunks();\n existingChunks.forEach(index => this.uploadedChunks.add(index));\n\n // 4. 上传分片\n await this.uploadChunksConcurrently();\n\n // 5. 完成上传\n const result = await this.completeUpload();\n this.status = UploadStatus.COMPLETED;\n this.options.onComplete(result);\n\n return result;\n } catch (error) {\n this.status = UploadStatus.FAILED;\n const err = error instanceof Error ? error : new Error(String(error));\n this.options.onError(err);\n throw err;\n }\n }\n\n /**\n * 暂停上传\n */\n pause(): void {\n if (this.status === UploadStatus.UPLOADING) {\n this.status = UploadStatus.PAUSED;\n if (this.abortController) {\n this.abortController.abort();\n }\n }\n }\n\n /**\n * 恢复上传\n */\n async resume(): Promise<CompleteUploadResponse> {\n if (this.status === UploadStatus.PAUSED) {\n return this.upload();\n }\n throw new Error('当前状态无法恢复上传');\n }\n\n /**\n * 取消上传\n */\n async cancel(): Promise<void> {\n if (this.taskId && this.status === UploadStatus.UPLOADING) {\n try {\n await this.request<ApiResponse<null>>(`/api/files/common/cancel/${this.taskId}`, {\n method: 'POST',\n headers: this.options.headers,\n });\n } catch (error) {\n console.warn('取消上传失败:', error);\n }\n }\n\n this.status = UploadStatus.CANCELLED;\n if (this.abortController) {\n this.abortController.abort();\n }\n }\n\n /**\n * 获取当前状态\n */\n getStatus(): UploadStatus {\n return this.status;\n }\n\n /**\n * 获取任务ID\n */\n getTaskId(): string | null {\n return this.taskId;\n }\n\n /**\n * HTTP请求封装\n */\n private async request<T>(url: string, options: RequestInit = {}): Promise<T> {\n const fullUrl = `${this.options.baseURL}${url}`;\n const signal = this.abortController?.signal;\n\n const response = await fetch(fullUrl, {\n ...options,\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP错误: ${response.status} ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /**\n * 延迟函数\n */\n private delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}\n\n/**\n * 创建文件上传器实例\n * @param file - 文件对象\n * @param options - 上传配置选项\n * @returns 上传器实例\n */\nexport function createUploader(file: File, options?: UploadOptions): ChunkUploader {\n return new ChunkUploader(file, options);\n}\n"]}
|