@giszhc/file-utils 0.0.1 → 0.0.3
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 +45 -1
- package/dist/file-utils.js +240 -186
- package/package.json +1 -1
- package/types/copy.d.ts +43 -0
- package/types/generate.d.ts +29 -4
- package/types/index.d.ts +5 -1
package/README.md
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
- **文件下载**:支持从 URL、Blob、Base64 等多种方式下载文件
|
|
9
9
|
- **文件转换**:支持文件格式转换(如 Base64 转 Blob、File 转 Base64 等)
|
|
10
10
|
- **文件压缩**:支持图片压缩处理
|
|
11
|
-
- **文件生成**:支持生成 TXT、CSV、JSON
|
|
11
|
+
- **文件生成**:支持生成 TXT、CSV、JSON 格式文件,可选择返回 File 对象而不下载
|
|
12
12
|
- **文件验证**:提供完整的文件验证和类型判断功能
|
|
13
|
+
- **剪贴板操作**:支持一键复制文本到剪贴板,并支持成功/失败回调
|
|
13
14
|
- **自动清理**:自动管理临时创建的 Blob URL,防止内存泄漏
|
|
14
15
|
- **TypeScript**:完善的类型定义支持
|
|
15
16
|
|
|
@@ -167,6 +168,12 @@ generateTxtFile('Hello World!', 'hello.txt');
|
|
|
167
168
|
// 生成多行文本
|
|
168
169
|
const lines = ['第一行', '第二行', '第三行'].join('\n');
|
|
169
170
|
generateTxtFile(lines, 'multiline.txt');
|
|
171
|
+
|
|
172
|
+
// 生成文件但不下载,返回 File 对象
|
|
173
|
+
const file = generateTxtFile('Hello World!', 'hello.txt', { download: false });
|
|
174
|
+
console.log(file.name); // "hello.txt"
|
|
175
|
+
console.log(file.type); // "text/plain;charset=utf-8"
|
|
176
|
+
console.log(file.size); // 文件大小(字节)
|
|
170
177
|
```
|
|
171
178
|
|
|
172
179
|
### 9. 生成 CSV 文件
|
|
@@ -192,6 +199,12 @@ generateCsvFile(data, 'data.csv');
|
|
|
192
199
|
|
|
193
200
|
// 自定义分隔符(制表符 TSV)
|
|
194
201
|
generateCsvFile(data, 'data.tsv', { separator: '\t' });
|
|
202
|
+
|
|
203
|
+
// 生成文件但不下载,返回 File 对象
|
|
204
|
+
const csvFile = generateCsvFile(users, 'users.csv', { download: false });
|
|
205
|
+
console.log(csvFile.name); // "users.csv"
|
|
206
|
+
console.log(csvFile.type); // "text/csv;charset=utf-8"
|
|
207
|
+
console.log(csvFile.size); // 文件大小(字节)
|
|
195
208
|
```
|
|
196
209
|
|
|
197
210
|
### 10. 生成 JSON 文件
|
|
@@ -215,6 +228,12 @@ generateJsonFile(data, 'data.min.json', { pretty: false });
|
|
|
215
228
|
|
|
216
229
|
// 自定义缩进
|
|
217
230
|
generateJsonFile(data, 'data.json', { spaces: 4 });
|
|
231
|
+
|
|
232
|
+
// 生成文件但不下载,返回 File 对象
|
|
233
|
+
const file = generateJsonFile(data, 'user.json', { download: false });
|
|
234
|
+
console.log(file.name); // "user.json"
|
|
235
|
+
console.log(file.type); // "application/json;charset=utf-8"
|
|
236
|
+
console.log(file.size); // 文件大小(字节)
|
|
218
237
|
```
|
|
219
238
|
|
|
220
239
|
### 11. 获取文件扩展名和文件名
|
|
@@ -336,6 +355,31 @@ console.log(data.name);
|
|
|
336
355
|
console.log(data.age);
|
|
337
356
|
```
|
|
338
357
|
|
|
358
|
+
### 19. 一键复制文本到剪贴板
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
import { copyToClipboard } from '@giszhc/file-utils';
|
|
362
|
+
|
|
363
|
+
// 基本使用
|
|
364
|
+
await copyToClipboard('Hello World!');
|
|
365
|
+
|
|
366
|
+
// 带回调的使用
|
|
367
|
+
await copyToClipboard('这是一段文本', {
|
|
368
|
+
onSuccess: () => console.log('复制成功!'),
|
|
369
|
+
onError: (error) => console.error('复制失败:', error)
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### 20. 从剪贴板读取文本
|
|
374
|
+
|
|
375
|
+
```ts
|
|
376
|
+
import { pasteFromClipboard } from '@giszhc/file-utils';
|
|
377
|
+
|
|
378
|
+
// 读取剪贴板内容
|
|
379
|
+
const text = await pasteFromClipboard();
|
|
380
|
+
console.log('剪贴板内容:', text);
|
|
381
|
+
```
|
|
382
|
+
|
|
339
383
|
------
|
|
340
384
|
|
|
341
385
|
## 使用方法
|
package/dist/file-utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
function
|
|
2
|
-
return new Promise((
|
|
1
|
+
function y(e, r = "text") {
|
|
2
|
+
return new Promise((t, n) => {
|
|
3
3
|
const o = new FileReader();
|
|
4
|
-
switch (
|
|
4
|
+
switch (r) {
|
|
5
5
|
case "text":
|
|
6
6
|
o.readAsText(e);
|
|
7
7
|
break;
|
|
@@ -19,203 +19,252 @@ function p(e, t = "text") {
|
|
|
19
19
|
}
|
|
20
20
|
o.onload = (i) => {
|
|
21
21
|
const a = i.target?.result;
|
|
22
|
-
a !== void 0 ?
|
|
22
|
+
a !== void 0 ? t(a) : n(new Error("文件读取失败"));
|
|
23
23
|
}, o.onerror = () => {
|
|
24
|
-
|
|
24
|
+
n(new Error("文件读取错误"));
|
|
25
25
|
}, o.onabort = () => {
|
|
26
|
-
|
|
26
|
+
n(new Error("文件读取被中止"));
|
|
27
27
|
};
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
-
async function
|
|
31
|
-
return new Promise((
|
|
30
|
+
async function T(e, r = "utf-8") {
|
|
31
|
+
return new Promise((t, n) => {
|
|
32
32
|
const o = new FileReader();
|
|
33
|
-
o.readAsText(e,
|
|
33
|
+
o.readAsText(e, r), o.onload = (i) => {
|
|
34
34
|
const a = i.target?.result;
|
|
35
|
-
a !== void 0 ?
|
|
35
|
+
a !== void 0 ? t(a) : n(new Error("文件读取失败"));
|
|
36
36
|
}, o.onerror = () => {
|
|
37
|
-
|
|
37
|
+
n(new Error("文件读取错误"));
|
|
38
38
|
}, o.onabort = () => {
|
|
39
|
-
|
|
39
|
+
n(new Error("文件读取被中止"));
|
|
40
40
|
};
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
|
-
async function
|
|
44
|
-
const { separator:
|
|
43
|
+
async function $(e, r = {}) {
|
|
44
|
+
const { separator: t = ",", hasHeader: n = !0 } = r, o = await y(e, "text");
|
|
45
45
|
if (!o.trim())
|
|
46
46
|
return [];
|
|
47
47
|
const i = o.split(/\r?\n/).filter((h) => h.trim() !== "");
|
|
48
48
|
if (i.length === 0)
|
|
49
49
|
return [];
|
|
50
50
|
let a = [], s = 0;
|
|
51
|
-
|
|
52
|
-
const
|
|
51
|
+
n && (a = F(i[0], t), s = 1);
|
|
52
|
+
const l = [];
|
|
53
53
|
for (let h = s; h < i.length; h++) {
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
56
|
-
const
|
|
57
|
-
a.forEach((
|
|
58
|
-
u
|
|
59
|
-
}),
|
|
54
|
+
const w = F(i[h], t);
|
|
55
|
+
if (n && a.length > 0) {
|
|
56
|
+
const d = {};
|
|
57
|
+
a.forEach((u, c) => {
|
|
58
|
+
d[u] = w[c] || "";
|
|
59
|
+
}), l.push(d);
|
|
60
60
|
} else
|
|
61
|
-
|
|
61
|
+
l.push(w);
|
|
62
62
|
}
|
|
63
|
-
return
|
|
63
|
+
return l;
|
|
64
64
|
}
|
|
65
|
-
function
|
|
66
|
-
const
|
|
67
|
-
let
|
|
65
|
+
function F(e, r) {
|
|
66
|
+
const t = [];
|
|
67
|
+
let n = "", o = !1;
|
|
68
68
|
for (let i = 0; i < e.length; i++) {
|
|
69
69
|
const a = e[i], s = e[i + 1];
|
|
70
|
-
a === '"' ? o && s === '"' ? (
|
|
70
|
+
a === '"' ? o && s === '"' ? (n += '"', i++) : o = !o : a === r && !o ? (t.push(n.trim()), n = "") : n += a;
|
|
71
71
|
}
|
|
72
|
-
return
|
|
72
|
+
return t.push(n.trim()), t;
|
|
73
73
|
}
|
|
74
|
-
async function
|
|
75
|
-
const
|
|
74
|
+
async function W(e) {
|
|
75
|
+
const r = await y(e, "text");
|
|
76
76
|
try {
|
|
77
|
-
return JSON.parse(
|
|
78
|
-
} catch (
|
|
79
|
-
throw new Error(`JSON 解析失败:${
|
|
77
|
+
return JSON.parse(r);
|
|
78
|
+
} catch (t) {
|
|
79
|
+
throw new Error(`JSON 解析失败:${t instanceof Error ? t.message : "未知错误"}`);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
-
async function
|
|
82
|
+
async function v(e, r, t) {
|
|
83
83
|
try {
|
|
84
|
-
const
|
|
85
|
-
if (!
|
|
86
|
-
throw new Error(`下载失败:${
|
|
87
|
-
const o = await
|
|
88
|
-
let i =
|
|
84
|
+
const n = await fetch(e, t?.fetchOptions);
|
|
85
|
+
if (!n.ok)
|
|
86
|
+
throw new Error(`下载失败:${n.status} ${n.statusText}`);
|
|
87
|
+
const o = await n.blob();
|
|
88
|
+
let i = r;
|
|
89
89
|
if (!i) {
|
|
90
|
-
const a =
|
|
90
|
+
const a = n.headers.get("Content-Disposition");
|
|
91
91
|
if (a) {
|
|
92
92
|
const s = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(a);
|
|
93
93
|
s && s[1] && (i = decodeURIComponent(s[1].replace(/['"]/g, "")));
|
|
94
94
|
}
|
|
95
95
|
i || (i = e.split("/").pop() || "download");
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
} catch (
|
|
99
|
-
throw console.error("文件下载失败:",
|
|
97
|
+
f(o, i, t);
|
|
98
|
+
} catch (n) {
|
|
99
|
+
throw console.error("文件下载失败:", n), n;
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
-
function
|
|
103
|
-
const
|
|
104
|
-
o.href =
|
|
102
|
+
function f(e, r, t) {
|
|
103
|
+
const n = URL.createObjectURL(e), o = document.createElement("a");
|
|
104
|
+
o.href = n, o.download = r, t?.newWindow && (o.target = "_blank"), document.body.appendChild(o), o.click(), document.body.removeChild(o), URL.revokeObjectURL(n);
|
|
105
105
|
}
|
|
106
|
-
function
|
|
107
|
-
e.startsWith("data:") ? (
|
|
106
|
+
function k(e, r, t) {
|
|
107
|
+
e.startsWith("data:") ? (t = e.split(",")[0].split(":")[1].split(";")[0], e = e.split(",")[1]) : t || (t = "application/octet-stream");
|
|
108
108
|
const o = atob(e), i = new Array(o.length);
|
|
109
|
-
for (let
|
|
110
|
-
i[
|
|
111
|
-
const a = new Uint8Array(i), s = new Blob([a], { type:
|
|
112
|
-
|
|
109
|
+
for (let l = 0; l < o.length; l++)
|
|
110
|
+
i[l] = o.charCodeAt(l);
|
|
111
|
+
const a = new Uint8Array(i), s = new Blob([a], { type: t });
|
|
112
|
+
f(s, r);
|
|
113
113
|
}
|
|
114
|
-
async function I(e,
|
|
115
|
-
for (let
|
|
114
|
+
async function I(e, r = 500) {
|
|
115
|
+
for (let t = 0; t < e.length; t++)
|
|
116
116
|
try {
|
|
117
|
-
await
|
|
118
|
-
} catch (
|
|
119
|
-
console.error(`第 ${
|
|
117
|
+
await v(e[t]), t < e.length - 1 && await new Promise((n) => setTimeout(n, r));
|
|
118
|
+
} catch (n) {
|
|
119
|
+
console.error(`第 ${t + 1} 个文件下载失败:`, n);
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
function L(e) {
|
|
123
|
-
return
|
|
123
|
+
return y(e, "dataURL");
|
|
124
124
|
}
|
|
125
|
-
function O(e,
|
|
126
|
-
e.startsWith("data:") ? (
|
|
127
|
-
const
|
|
128
|
-
for (let a = 0; a <
|
|
129
|
-
o[a] =
|
|
125
|
+
function O(e, r) {
|
|
126
|
+
e.startsWith("data:") ? (r = e.split(",")[0].split(":")[1].split(";")[0], e = e.split(",")[1]) : r || (r = "application/octet-stream");
|
|
127
|
+
const n = atob(e), o = new Array(n.length);
|
|
128
|
+
for (let a = 0; a < n.length; a++)
|
|
129
|
+
o[a] = n.charCodeAt(a);
|
|
130
130
|
const i = new Uint8Array(o);
|
|
131
|
-
return new Blob([i], { type:
|
|
131
|
+
return new Blob([i], { type: r });
|
|
132
132
|
}
|
|
133
|
-
function R(e,
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
async function R(e, r) {
|
|
134
|
+
try {
|
|
135
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
136
|
+
await navigator.clipboard.writeText(e), r?.onSuccess && r.onSuccess(e);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const t = document.createElement("textarea");
|
|
140
|
+
t.value = e, t.style.position = "fixed", t.style.top = "0", t.style.left = "0", t.style.width = "2em", t.style.height = "2em", t.style.padding = "0", t.style.border = "none", t.style.outline = "none", t.style.boxShadow = "none", t.style.background = "transparent", t.style.opacity = "0", document.body.appendChild(t), t.focus(), t.select();
|
|
141
|
+
try {
|
|
142
|
+
if (document.execCommand("copy"))
|
|
143
|
+
r?.onSuccess && r.onSuccess(e);
|
|
144
|
+
else
|
|
145
|
+
throw new Error("execCommand 复制失败");
|
|
146
|
+
} catch {
|
|
147
|
+
throw new Error("无法使用 execCommand 复制文本");
|
|
148
|
+
} finally {
|
|
149
|
+
document.body.removeChild(t);
|
|
150
|
+
}
|
|
151
|
+
} catch (t) {
|
|
152
|
+
const n = t instanceof Error ? t : new Error("未知错误");
|
|
153
|
+
throw r?.onError ? r.onError(n) : console.error("复制到剪贴板失败:", n), n;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function j() {
|
|
157
|
+
try {
|
|
158
|
+
if (navigator.clipboard && navigator.clipboard.readText)
|
|
159
|
+
return await navigator.clipboard.readText();
|
|
160
|
+
throw new Error("当前浏览器不支持读取剪贴板");
|
|
161
|
+
} catch (e) {
|
|
162
|
+
throw console.error("从剪贴板读取失败:", e), new Error("无法从剪贴板读取内容,请检查浏览器权限");
|
|
163
|
+
}
|
|
136
164
|
}
|
|
137
|
-
function
|
|
165
|
+
function z(e, r, t) {
|
|
166
|
+
const {
|
|
167
|
+
download: n = !0,
|
|
168
|
+
encoding: o = "utf-8"
|
|
169
|
+
} = t || {}, i = new Blob([e], { type: `text/plain;charset=${o}` });
|
|
170
|
+
if (n) {
|
|
171
|
+
f(i, r);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
return new File([i], r, { type: `text/plain;charset=${o}` });
|
|
175
|
+
}
|
|
176
|
+
function p(e, r) {
|
|
138
177
|
if (e == null)
|
|
139
178
|
return "";
|
|
140
|
-
const
|
|
141
|
-
return
|
|
142
|
-
`) ||
|
|
179
|
+
const t = String(e);
|
|
180
|
+
return t.includes(r) || t.includes('"') || t.includes(`
|
|
181
|
+
`) || t.includes("\r") ? `"${t.replace(/"/g, '""')}"` : t;
|
|
143
182
|
}
|
|
144
|
-
function
|
|
183
|
+
function U(e, r, t = {}) {
|
|
145
184
|
const {
|
|
146
|
-
separator:
|
|
185
|
+
separator: n = ",",
|
|
147
186
|
includeHeader: o = !0,
|
|
148
|
-
|
|
149
|
-
|
|
187
|
+
download: i = !0,
|
|
188
|
+
encoding: a = "utf-8"
|
|
189
|
+
} = t;
|
|
150
190
|
if (e.length === 0)
|
|
151
191
|
throw new Error("数据不能为空");
|
|
152
|
-
let
|
|
192
|
+
let s = "";
|
|
153
193
|
if (typeof e[0] == "object" && !Array.isArray(e[0])) {
|
|
154
|
-
const
|
|
155
|
-
new Set(
|
|
194
|
+
const d = e, u = Array.from(
|
|
195
|
+
new Set(d.flatMap((c) => Object.keys(c)))
|
|
156
196
|
);
|
|
157
|
-
o && (
|
|
158
|
-
`),
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
return
|
|
197
|
+
o && (s += u.join(n) + `
|
|
198
|
+
`), d.forEach((c) => {
|
|
199
|
+
const g = u.map((b) => {
|
|
200
|
+
const A = c[b];
|
|
201
|
+
return p(A, n);
|
|
162
202
|
});
|
|
163
|
-
|
|
203
|
+
s += g.join(n) + `
|
|
164
204
|
`;
|
|
165
205
|
});
|
|
166
206
|
} else {
|
|
167
|
-
const
|
|
168
|
-
if (o &&
|
|
169
|
-
const u =
|
|
170
|
-
(c) =>
|
|
207
|
+
const d = e;
|
|
208
|
+
if (o && d.length > 0) {
|
|
209
|
+
const u = d[0].map(
|
|
210
|
+
(c) => p(c, n)
|
|
171
211
|
);
|
|
172
|
-
|
|
212
|
+
s += u.join(n) + `
|
|
173
213
|
`;
|
|
174
|
-
for (let c = 1; c <
|
|
175
|
-
const
|
|
176
|
-
(
|
|
214
|
+
for (let c = 1; c < d.length; c++) {
|
|
215
|
+
const g = d[c].map(
|
|
216
|
+
(b) => p(b, n)
|
|
177
217
|
);
|
|
178
|
-
|
|
218
|
+
s += g.join(n) + `
|
|
179
219
|
`;
|
|
180
220
|
}
|
|
181
|
-
} else o ||
|
|
221
|
+
} else o || d.forEach((u) => {
|
|
182
222
|
const c = u.map(
|
|
183
|
-
(
|
|
223
|
+
(g) => p(g, n)
|
|
184
224
|
);
|
|
185
|
-
|
|
225
|
+
s += c.join(n) + `
|
|
186
226
|
`;
|
|
187
227
|
});
|
|
188
228
|
}
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
type: `text/csv;charset=${
|
|
229
|
+
s = s.trimEnd();
|
|
230
|
+
const h = "\uFEFF", w = new Blob([h + s], {
|
|
231
|
+
type: `text/csv;charset=${a}`
|
|
192
232
|
});
|
|
193
|
-
|
|
233
|
+
if (i) {
|
|
234
|
+
f(w, r);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
return new File([w], r, { type: `text/csv;charset=${a}` });
|
|
194
238
|
}
|
|
195
|
-
function
|
|
239
|
+
function H(e, r, t = {}) {
|
|
196
240
|
const {
|
|
197
|
-
pretty:
|
|
241
|
+
pretty: n = !0,
|
|
198
242
|
spaces: o = 2,
|
|
199
|
-
|
|
200
|
-
|
|
243
|
+
download: i = !0,
|
|
244
|
+
encoding: a = "utf-8"
|
|
245
|
+
} = t;
|
|
201
246
|
try {
|
|
202
|
-
const
|
|
203
|
-
type: `application/json;charset=${
|
|
247
|
+
const s = n ? JSON.stringify(e, null, o) : JSON.stringify(e), l = new Blob([s], {
|
|
248
|
+
type: `application/json;charset=${a}`
|
|
204
249
|
});
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
250
|
+
if (i) {
|
|
251
|
+
f(l, r);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
return new File([l], r, { type: `application/json;charset=${a}` });
|
|
255
|
+
} catch (s) {
|
|
256
|
+
throw console.error("JSON 序列化失败:", s), new Error("数据无法序列化为 JSON");
|
|
208
257
|
}
|
|
209
258
|
}
|
|
210
|
-
function
|
|
211
|
-
const
|
|
212
|
-
return
|
|
259
|
+
function x(e) {
|
|
260
|
+
const r = typeof e == "string" ? e : e.name, t = r.lastIndexOf(".");
|
|
261
|
+
return t === -1 || t === r.length - 1 ? "" : r.slice(t).toLowerCase();
|
|
213
262
|
}
|
|
214
|
-
function
|
|
215
|
-
const
|
|
216
|
-
return
|
|
263
|
+
function J(e) {
|
|
264
|
+
const r = typeof e == "string" ? e : e.name, t = r.lastIndexOf(".");
|
|
265
|
+
return t === -1 ? r : r.slice(0, t);
|
|
217
266
|
}
|
|
218
|
-
function
|
|
267
|
+
function M(e) {
|
|
219
268
|
return {
|
|
220
269
|
name: e.name,
|
|
221
270
|
size: e.size,
|
|
@@ -223,127 +272,132 @@ function j(e) {
|
|
|
223
272
|
lastModified: e.lastModified
|
|
224
273
|
};
|
|
225
274
|
}
|
|
226
|
-
function
|
|
227
|
-
if (!e || !
|
|
275
|
+
function E(e, r) {
|
|
276
|
+
if (!e || !r || r.length === 0)
|
|
228
277
|
return !1;
|
|
229
|
-
const
|
|
230
|
-
return
|
|
278
|
+
const t = e.type.toLowerCase(), n = x(e).toLowerCase();
|
|
279
|
+
return r.some((o) => {
|
|
231
280
|
const i = o.toLowerCase();
|
|
232
281
|
if (i.endsWith("/*")) {
|
|
233
282
|
const a = i.split("/")[0];
|
|
234
|
-
return
|
|
283
|
+
return t.startsWith(`${a}/`);
|
|
235
284
|
}
|
|
236
|
-
return i.startsWith(".") ?
|
|
285
|
+
return i.startsWith(".") ? n === i : t === i;
|
|
237
286
|
});
|
|
238
287
|
}
|
|
239
|
-
function
|
|
240
|
-
return e ? e.size <=
|
|
288
|
+
function C(e, r) {
|
|
289
|
+
return e ? e.size <= r : !1;
|
|
241
290
|
}
|
|
242
291
|
function m(e) {
|
|
243
292
|
if (!e)
|
|
244
293
|
return !1;
|
|
245
294
|
if (e.type.startsWith("image/"))
|
|
246
295
|
return !0;
|
|
247
|
-
const
|
|
248
|
-
return
|
|
296
|
+
const r = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg", ".ico"], t = x(e);
|
|
297
|
+
return r.includes(t);
|
|
249
298
|
}
|
|
250
299
|
function B(e) {
|
|
251
|
-
return new Promise((
|
|
300
|
+
return new Promise((r, t) => {
|
|
252
301
|
if (!m(e)) {
|
|
253
|
-
|
|
302
|
+
t(new Error("文件不是有效的图片格式"));
|
|
254
303
|
return;
|
|
255
304
|
}
|
|
256
|
-
const
|
|
257
|
-
|
|
305
|
+
const n = new FileReader();
|
|
306
|
+
n.onload = (o) => {
|
|
258
307
|
const i = new Image();
|
|
259
308
|
i.onload = () => {
|
|
260
|
-
|
|
309
|
+
r({
|
|
261
310
|
width: i.width,
|
|
262
311
|
height: i.height
|
|
263
312
|
});
|
|
264
313
|
}, i.onerror = () => {
|
|
265
|
-
|
|
314
|
+
t(new Error("图片加载失败"));
|
|
266
315
|
}, i.src = o.target?.result;
|
|
267
|
-
},
|
|
268
|
-
|
|
269
|
-
},
|
|
316
|
+
}, n.onerror = () => {
|
|
317
|
+
t(new Error("文件读取失败"));
|
|
318
|
+
}, n.readAsDataURL(e);
|
|
270
319
|
});
|
|
271
320
|
}
|
|
272
|
-
async function
|
|
273
|
-
const
|
|
274
|
-
if (
|
|
275
|
-
const
|
|
276
|
-
|
|
321
|
+
async function N(e, r = {}) {
|
|
322
|
+
const t = [];
|
|
323
|
+
if (r.acceptTypes && r.acceptTypes.length > 0 && (E(e, r.acceptTypes) || t.push(`不支持的文件类型。接受:${r.acceptTypes.join(", ")}`)), r.maxSize !== void 0 && !C(e, r.maxSize)) {
|
|
324
|
+
const n = S(r.maxSize);
|
|
325
|
+
t.push(`文件过大。最大允许:${n}`);
|
|
277
326
|
}
|
|
278
|
-
if (
|
|
327
|
+
if (r.mustBeImage && !m(e) && t.push("文件必须是图片格式"), m(e) && (r.minWidth !== void 0 || r.maxWidth !== void 0 || r.minHeight !== void 0 || r.maxHeight !== void 0))
|
|
279
328
|
try {
|
|
280
|
-
const
|
|
281
|
-
|
|
329
|
+
const n = await B(e);
|
|
330
|
+
r.minWidth !== void 0 && n.width < r.minWidth && t.push(`图片宽度过小。最小宽度:${r.minWidth}px`), r.maxWidth !== void 0 && n.width > r.maxWidth && t.push(`图片宽度过大。最大宽度:${r.maxWidth}px`), r.minHeight !== void 0 && n.height < r.minHeight && t.push(`图片高度过小。最小高度:${r.minHeight}px`), r.maxHeight !== void 0 && n.height > r.maxHeight && t.push(`图片高度过大。最大高度:${r.maxHeight}px`);
|
|
282
331
|
} catch {
|
|
283
|
-
|
|
332
|
+
t.push("无法获取图片尺寸");
|
|
284
333
|
}
|
|
285
334
|
return {
|
|
286
|
-
valid:
|
|
287
|
-
errors:
|
|
335
|
+
valid: t.length === 0,
|
|
336
|
+
errors: t
|
|
288
337
|
};
|
|
289
338
|
}
|
|
290
|
-
function
|
|
339
|
+
function S(e, r = 2) {
|
|
291
340
|
if (e === 0) return "0 B";
|
|
292
|
-
const
|
|
293
|
-
return parseFloat((e / Math.pow(
|
|
341
|
+
const t = ["B", "KB", "MB", "GB", "TB"], n = 1024, o = Math.floor(Math.log(e) / Math.log(n));
|
|
342
|
+
return parseFloat((e / Math.pow(n, o)).toFixed(r)) + " " + t[o];
|
|
294
343
|
}
|
|
295
|
-
const
|
|
344
|
+
const D = {
|
|
296
345
|
// 读取
|
|
297
|
-
readFile:
|
|
298
|
-
readTxtFile:
|
|
299
|
-
readCsvFile:
|
|
300
|
-
readJsonFile:
|
|
346
|
+
readFile: y,
|
|
347
|
+
readTxtFile: T,
|
|
348
|
+
readCsvFile: $,
|
|
349
|
+
readJsonFile: W,
|
|
301
350
|
// 下载
|
|
302
|
-
downloadFile:
|
|
303
|
-
downloadBlob:
|
|
304
|
-
downloadBase64:
|
|
351
|
+
downloadFile: v,
|
|
352
|
+
downloadBlob: f,
|
|
353
|
+
downloadBase64: k,
|
|
305
354
|
downloadMultiple: I,
|
|
306
355
|
// 转换
|
|
307
356
|
fileToBase64: L,
|
|
308
357
|
base64ToBlob: O,
|
|
358
|
+
// 剪贴板
|
|
359
|
+
copyToClipboard: R,
|
|
360
|
+
pasteFromClipboard: j,
|
|
309
361
|
// 生成
|
|
310
|
-
generateTxtFile:
|
|
311
|
-
generateCsvFile:
|
|
312
|
-
generateJsonFile:
|
|
362
|
+
generateTxtFile: z,
|
|
363
|
+
generateCsvFile: U,
|
|
364
|
+
generateJsonFile: H,
|
|
313
365
|
// 验证
|
|
314
|
-
checkFileType:
|
|
315
|
-
checkFileSize:
|
|
366
|
+
checkFileType: E,
|
|
367
|
+
checkFileSize: C,
|
|
316
368
|
isImage: m,
|
|
317
369
|
getImageDimensions: B,
|
|
318
|
-
validateFile:
|
|
319
|
-
formatFileSize:
|
|
370
|
+
validateFile: N,
|
|
371
|
+
formatFileSize: S,
|
|
320
372
|
// 工具
|
|
321
|
-
getFileExtension:
|
|
322
|
-
getFileNameWithoutExtension:
|
|
323
|
-
getFileInfo:
|
|
373
|
+
getFileExtension: x,
|
|
374
|
+
getFileNameWithoutExtension: J,
|
|
375
|
+
getFileInfo: M
|
|
324
376
|
};
|
|
325
377
|
export {
|
|
326
378
|
O as base64ToBlob,
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
379
|
+
C as checkFileSize,
|
|
380
|
+
E as checkFileType,
|
|
381
|
+
R as copyToClipboard,
|
|
382
|
+
D as default,
|
|
383
|
+
k as downloadBase64,
|
|
384
|
+
f as downloadBlob,
|
|
385
|
+
v as downloadFile,
|
|
333
386
|
I as downloadMultiple,
|
|
334
387
|
L as fileToBase64,
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
388
|
+
S as formatFileSize,
|
|
389
|
+
U as generateCsvFile,
|
|
390
|
+
H as generateJsonFile,
|
|
391
|
+
z as generateTxtFile,
|
|
392
|
+
x as getFileExtension,
|
|
393
|
+
M as getFileInfo,
|
|
394
|
+
J as getFileNameWithoutExtension,
|
|
342
395
|
B as getImageDimensions,
|
|
343
396
|
m as isImage,
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
397
|
+
j as pasteFromClipboard,
|
|
398
|
+
$ as readCsvFile,
|
|
399
|
+
y as readFile,
|
|
400
|
+
W as readJsonFile,
|
|
401
|
+
T as readTxtFile,
|
|
402
|
+
N as validateFile
|
|
349
403
|
};
|
package/package.json
CHANGED
package/types/copy.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 剪贴板复制相关方法
|
|
3
|
+
*/
|
|
4
|
+
import type { CopyOptions } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* 一键复制文本到剪贴板
|
|
7
|
+
*
|
|
8
|
+
* @param text - 要复制的文本内容
|
|
9
|
+
* @param options - 复制选项配置(可选)
|
|
10
|
+
* @returns Promise<void>
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // 基本使用
|
|
14
|
+
* copyToClipboard('Hello World!');
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // 带回调的使用
|
|
18
|
+
* copyToClipboard('Hello World!', {
|
|
19
|
+
* onSuccess: () => console.log('复制成功!'),
|
|
20
|
+
* onError: (error) => console.error('复制失败:', error)
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* - 优先使用现代的 navigator.clipboard API
|
|
25
|
+
* - 自动降级到传统的 execCommand 方式以兼容旧浏览器
|
|
26
|
+
* - 需要用户授权才能访问剪贴板
|
|
27
|
+
*/
|
|
28
|
+
export declare function copyToClipboard(text: string, options?: CopyOptions): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* 从剪贴板读取文本
|
|
31
|
+
*
|
|
32
|
+
* @returns Promise<string> - 剪贴板中的文本内容
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // 读取剪贴板内容
|
|
36
|
+
* const text = await pasteFromClipboard();
|
|
37
|
+
* console.log('剪贴板内容:', text);
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* - 需要用户授权才能访问剪贴板
|
|
41
|
+
* - 在某些浏览器中可能需要 HTTPS 环境
|
|
42
|
+
*/
|
|
43
|
+
export declare function pasteFromClipboard(): Promise<string>;
|
package/types/generate.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 文件生成相关方法
|
|
3
3
|
*/
|
|
4
|
+
import type { GenerateOptions } from './types';
|
|
4
5
|
/**
|
|
5
6
|
* 生成并下载 TXT 文件
|
|
6
7
|
*
|
|
7
8
|
* @param content - 文本内容
|
|
8
9
|
* @param filename - 文件名(包含 .txt 扩展名)
|
|
9
|
-
* @param
|
|
10
|
+
* @param options - 生成选项配置(可选)
|
|
11
|
+
* - download: 是否直接下载,默认 true
|
|
12
|
+
* - encoding: 字符编码,默认 'utf-8'
|
|
13
|
+
*
|
|
14
|
+
* @returns 如果 options.download 为 false,返回 File 对象;否则返回 void
|
|
10
15
|
*
|
|
11
16
|
* @example
|
|
12
17
|
* // 生成简单的文本文件
|
|
@@ -16,8 +21,12 @@
|
|
|
16
21
|
* // 生成多行文本
|
|
17
22
|
* const lines = ['第一行', '第二行', '第三行'].join('\n');
|
|
18
23
|
* generateTxtFile(lines, 'multiline.txt');
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // 生成文件但不下载,返回 File 对象
|
|
27
|
+
* const file = generateTxtFile('Hello World!', 'hello.txt', { download: false });
|
|
19
28
|
*/
|
|
20
|
-
export declare function generateTxtFile(content: string, filename: string,
|
|
29
|
+
export declare function generateTxtFile(content: string, filename: string, options?: GenerateOptions): File | void;
|
|
21
30
|
/**
|
|
22
31
|
* 生成并下载 CSV 文件
|
|
23
32
|
*
|
|
@@ -26,8 +35,11 @@ export declare function generateTxtFile(content: string, filename: string, encod
|
|
|
26
35
|
* @param options - CSV 选项配置(可选)
|
|
27
36
|
* - separator: 字段分隔符,默认 ','
|
|
28
37
|
* - includeHeader: 是否包含表头,默认 true
|
|
38
|
+
* - download: 是否直接下载,默认 true
|
|
29
39
|
* - encoding: 字符编码,默认 'utf-8'
|
|
30
40
|
*
|
|
41
|
+
* @returns 如果 options.download 为 false,返回 File 对象;否则返回 void
|
|
42
|
+
*
|
|
31
43
|
* @example
|
|
32
44
|
* // 使用对象数组生成 CSV
|
|
33
45
|
* const users = [
|
|
@@ -49,12 +61,17 @@ export declare function generateTxtFile(content: string, filename: string, encod
|
|
|
49
61
|
* @example
|
|
50
62
|
* // 自定义分隔符(制表符)
|
|
51
63
|
* generateCsvFile(data, 'data.tsv', { separator: '\t' });
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* // 生成文件但不下载,返回 File 对象
|
|
67
|
+
* const file = generateCsvFile(users, 'users.csv', { download: false });
|
|
52
68
|
*/
|
|
53
69
|
export declare function generateCsvFile(data: Record<string, any>[] | any[][], filename: string, options?: {
|
|
54
70
|
separator?: string;
|
|
55
71
|
includeHeader?: boolean;
|
|
72
|
+
download?: boolean;
|
|
56
73
|
encoding?: string;
|
|
57
|
-
}): void;
|
|
74
|
+
}): File | void;
|
|
58
75
|
/**
|
|
59
76
|
* 生成并下载 JSON 文件
|
|
60
77
|
*
|
|
@@ -63,8 +80,11 @@ export declare function generateCsvFile(data: Record<string, any>[] | any[][], f
|
|
|
63
80
|
* @param options - JSON 选项配置(可选)
|
|
64
81
|
* - pretty: 是否格式化输出,默认 true
|
|
65
82
|
* - spaces: 缩进空格数,默认 2
|
|
83
|
+
* - download: 是否直接下载,默认 true
|
|
66
84
|
* - encoding: 字符编码,默认 'utf-8'
|
|
67
85
|
*
|
|
86
|
+
* @returns 如果 options.download 为 false,返回 File 对象;否则返回 void
|
|
87
|
+
*
|
|
68
88
|
* @example
|
|
69
89
|
* // 生成简单的 JSON 文件
|
|
70
90
|
* const data = { name: '张三', age: 25 };
|
|
@@ -85,9 +105,14 @@ export declare function generateCsvFile(data: Record<string, any>[] | any[][], f
|
|
|
85
105
|
* @example
|
|
86
106
|
* // 自定义缩进
|
|
87
107
|
* generateJsonFile(data, 'data.json', { spaces: 4 });
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* // 生成文件但不下载,返回 File 对象
|
|
111
|
+
* const file = generateJsonFile(data, 'user.json', { download: false });
|
|
88
112
|
*/
|
|
89
113
|
export declare function generateJsonFile(data: any, filename: string, options?: {
|
|
90
114
|
pretty?: boolean;
|
|
91
115
|
spaces?: number;
|
|
116
|
+
download?: boolean;
|
|
92
117
|
encoding?: string;
|
|
93
|
-
}): void;
|
|
118
|
+
}): File | void;
|
package/types/index.d.ts
CHANGED
|
@@ -4,16 +4,18 @@
|
|
|
4
4
|
* 提供常用的文件处理功能:读取、下载、转换、生成、验证等
|
|
5
5
|
* 支持 Tree Shaking,按需引入
|
|
6
6
|
*/
|
|
7
|
-
export type { ReadFileType, CompressOptions, DownloadOptions, IFileInfo, IValidationResult, IFileValidationOptions } from './types';
|
|
7
|
+
export type { ReadFileType, CompressOptions, DownloadOptions, CopyOptions, IFileInfo, IValidationResult, IFileValidationOptions } from './types';
|
|
8
8
|
export { readFile, readTxtFile, readCsvFile, readJsonFile } from './read';
|
|
9
9
|
export { downloadFile, downloadBlob, downloadBase64, downloadMultiple } from './download';
|
|
10
10
|
export { fileToBase64, base64ToBlob } from './convert';
|
|
11
|
+
export { copyToClipboard, pasteFromClipboard } from './copy';
|
|
11
12
|
export { generateTxtFile, generateCsvFile, generateJsonFile } from './generate';
|
|
12
13
|
export { checkFileType, checkFileSize, isImage, getImageDimensions, validateFile, formatFileSize } from './validators';
|
|
13
14
|
export { getFileExtension, getFileNameWithoutExtension, getFileInfo } from './utils';
|
|
14
15
|
import { readFile, readTxtFile, readCsvFile, readJsonFile } from './read';
|
|
15
16
|
import { downloadFile, downloadBlob, downloadBase64, downloadMultiple } from './download';
|
|
16
17
|
import { fileToBase64, base64ToBlob } from './convert';
|
|
18
|
+
import { copyToClipboard, pasteFromClipboard } from './copy';
|
|
17
19
|
import { generateTxtFile, generateCsvFile, generateJsonFile } from './generate';
|
|
18
20
|
import { checkFileType, checkFileSize, isImage, getImageDimensions, validateFile, formatFileSize } from './validators';
|
|
19
21
|
import { getFileExtension, getFileNameWithoutExtension, getFileInfo } from './utils';
|
|
@@ -28,6 +30,8 @@ declare const _default: {
|
|
|
28
30
|
downloadMultiple: typeof downloadMultiple;
|
|
29
31
|
fileToBase64: typeof fileToBase64;
|
|
30
32
|
base64ToBlob: typeof base64ToBlob;
|
|
33
|
+
copyToClipboard: typeof copyToClipboard;
|
|
34
|
+
pasteFromClipboard: typeof pasteFromClipboard;
|
|
31
35
|
generateTxtFile: typeof generateTxtFile;
|
|
32
36
|
generateCsvFile: typeof generateCsvFile;
|
|
33
37
|
generateJsonFile: typeof generateJsonFile;
|