@hzab/utils 0.0.1 → 1.0.0-beta
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/package.json +1 -1
- package/src/base64Str.ts +49 -0
- package/src/blob.ts +42 -0
- package/src/empty-val.ts +40 -0
- package/src/file/fileName.ts +3 -1
- package/src/string.ts +21 -9
- package/src/upload/OfflineUpload.ts +4 -2
- package/src/upload/fileDataNormalization.ts +281 -0
- package/src/upload/ossUpload.ts +4 -2
- package/src/upload/uploadUtils.ts +88 -15
package/package.json
CHANGED
package/src/base64Str.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 是否是 base64 字符串
|
|
3
|
+
* @param {*} str
|
|
4
|
+
* @returns
|
|
5
|
+
*/
|
|
6
|
+
export const isBase64Str = function (str) {
|
|
7
|
+
if (typeof str !== "string") {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
return /^data:\w+\/\w+;base64/i.test(str);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 从 base64 字符串获取文件类型
|
|
15
|
+
* @param base64Str
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
export function getFileTypeFromBase64(base64Str) {
|
|
19
|
+
if (!isBase64Str(base64Str)) {
|
|
20
|
+
return base64Str;
|
|
21
|
+
}
|
|
22
|
+
// 移除Base64前缀(如果存在)
|
|
23
|
+
const base64Data = base64Str.replace(/^data:.*;base64,/, "");
|
|
24
|
+
|
|
25
|
+
// 键值对调换:Base64前缀作为键,文件类型信息作为值
|
|
26
|
+
const prefixMap = {
|
|
27
|
+
JVBERi0: { type: "pdf", mimeType: "application/pdf" },
|
|
28
|
+
R0lGODdh: { type: "gif", mimeType: "image/gif" },
|
|
29
|
+
R0lGODlh: { type: "gif", mimeType: "image/gif" },
|
|
30
|
+
iVBORw0KGgo: { type: "png", mimeType: "image/png" },
|
|
31
|
+
"/9j/": { type: "jpeg", mimeType: "image/jpeg" },
|
|
32
|
+
SUQzB: { type: "mp4", mimeType: "video/mp4" },
|
|
33
|
+
AAAAGGZ0eXBpc29tAA: { type: "mpeg", mimeType: "audio/mpeg" },
|
|
34
|
+
UklGR: { type: "webp", mimeType: "image/webp" },
|
|
35
|
+
PD94bWwgdmVyc2lvbj0: { type: "xml", mimeType: "text/xml" },
|
|
36
|
+
PHN2ZyB4bWxucz0: { type: "svg", mimeType: "image/svg+xml" },
|
|
37
|
+
dGV4dCB4bWxucz0: { type: "txt", mimeType: "text/plain" },
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// 使用Object.keys遍历所有前缀,一次循环完成检测
|
|
41
|
+
for (const prefix of Object.keys(prefixMap)) {
|
|
42
|
+
if (base64Data.startsWith(prefix)) {
|
|
43
|
+
return prefixMap[prefix];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 未匹配到已知类型
|
|
48
|
+
return { type: "unknown", mimeType: "application/octet-stream" };
|
|
49
|
+
}
|
package/src/blob.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通过 Blob 的文件签名判断文件类型
|
|
3
|
+
* @param {Blob} blob - 待检测的 Blob 对象
|
|
4
|
+
* @returns {Promise<string>} - 匹配的 MIME 类型或 'unknown'
|
|
5
|
+
*/
|
|
6
|
+
export async function getFileTypeFromBlob(blob: Blob): Promise<string> {
|
|
7
|
+
// 预设文件签名表(键:十六进制前缀,值:MIME类型)
|
|
8
|
+
const fileSignatures = {
|
|
9
|
+
"89504E47": "image/png",
|
|
10
|
+
FFD8FF: "image/jpeg",
|
|
11
|
+
"47494638": "image/gif",
|
|
12
|
+
"25504446": "application/pdf",
|
|
13
|
+
"504B0304": "application/zip", // ZIP 或 DOCX/XLSX 等基于 ZIP 的格式
|
|
14
|
+
"3C3F786D6C": "text/xml", // XML
|
|
15
|
+
"7B22": "application/json", // JSON(开头可能为 { 或 {")
|
|
16
|
+
"25215053": "application/postscript", // PS
|
|
17
|
+
"494433": "audio/mpeg", // MP3
|
|
18
|
+
"0000001866747970": "video/mp4", // MP4(部分情况)
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// 读取 Blob 的前 8 字节(足够覆盖大部分签名)
|
|
22
|
+
const buffer = await blob.slice(0, 8).arrayBuffer();
|
|
23
|
+
const uint8Array = new Uint8Array(buffer);
|
|
24
|
+
|
|
25
|
+
// 转换为十六进制字符串(大写)
|
|
26
|
+
let hex = "";
|
|
27
|
+
for (let i = 0; i < uint8Array.length; i++) {
|
|
28
|
+
const byte = uint8Array[i].toString(16).padStart(2, "0");
|
|
29
|
+
hex += byte.toUpperCase();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 对比签名表(优先匹配长前缀)
|
|
33
|
+
const signatures = Object.keys(fileSignatures).sort((a, b) => b.length - a.length);
|
|
34
|
+
for (const signature of signatures) {
|
|
35
|
+
if (hex.startsWith(signature)) {
|
|
36
|
+
return fileSignatures[signature];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 未匹配到已知类型
|
|
41
|
+
return "unknown";
|
|
42
|
+
}
|
package/src/empty-val.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface ICheckEmptyOpt {
|
|
2
|
+
/** 【废弃】空值占位符 */
|
|
3
|
+
emptyStr?: string;
|
|
4
|
+
/** 空值占位符 */
|
|
5
|
+
emptySymbol?: string;
|
|
6
|
+
checkEmptyArr?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface IHandleEmptyValOpt extends ICheckEmptyOpt {}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 空数据判断
|
|
12
|
+
* @param {*} val
|
|
13
|
+
* @param {*} opt
|
|
14
|
+
* @returns
|
|
15
|
+
*/
|
|
16
|
+
export function checkEmpty(val, opt: ICheckEmptyOpt = {}) {
|
|
17
|
+
const { checkEmptyArr = true } = opt || {};
|
|
18
|
+
if (val === "" || val === null || val === undefined) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (checkEmptyArr && Array.isArray(val) && Array.length === 0) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 处理空数据显示占位符
|
|
29
|
+
* @param val
|
|
30
|
+
* @param opt
|
|
31
|
+
* @returns
|
|
32
|
+
*/
|
|
33
|
+
export const handleEmptyVal = (val, opt: IHandleEmptyValOpt = {}) => {
|
|
34
|
+
const { emptyStr = "--" } = opt || {};
|
|
35
|
+
const emptySymbol = opt?.emptySymbol ?? emptyStr;
|
|
36
|
+
if (checkEmpty(val, opt)) {
|
|
37
|
+
return emptySymbol;
|
|
38
|
+
}
|
|
39
|
+
return val;
|
|
40
|
+
};
|
package/src/file/fileName.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
1
2
|
import { nanoidNumALetters } from "../nanoid";
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -62,6 +63,7 @@ export const mergeFileName = function (fileName, str = "") {
|
|
|
62
63
|
}
|
|
63
64
|
const name = fileName?.match(/([^\/]+?)\.[^\/]+$/)?.[1] || "";
|
|
64
65
|
const suffix = fileName?.match(/[^\/]+?\.([^\/]+)$/)?.[1] || "";
|
|
66
|
+
|
|
65
67
|
return `${name}${str ?? ""}.${suffix}`;
|
|
66
68
|
};
|
|
67
69
|
|
|
@@ -71,5 +73,5 @@ export const mergeFileName = function (fileName, str = "") {
|
|
|
71
73
|
* @returns
|
|
72
74
|
*/
|
|
73
75
|
export const getUFileName = function (fileName) {
|
|
74
|
-
return mergeFileName(getFileName(fileName) || "
|
|
76
|
+
return mergeFileName(getFileName(fileName) || "up", "-" + `~kid-${nanoidNumALetters()}~`);
|
|
75
77
|
};
|
package/src/string.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 是否是 base64 字符串
|
|
3
|
-
* @param {*} str
|
|
4
|
-
* @returns
|
|
5
|
-
*/
|
|
6
|
-
export const isBase64Str = function (str) {
|
|
7
|
-
return /^data:\w+\/\w+;base64/i.test(str);
|
|
8
|
-
};
|
|
9
|
-
|
|
10
1
|
/**
|
|
11
2
|
* 字符串中划线转为小驼峰
|
|
12
3
|
* @param s
|
|
@@ -17,3 +8,24 @@ export const convertToCamelCase = function (s) {
|
|
|
17
8
|
return letter.toUpperCase();
|
|
18
9
|
});
|
|
19
10
|
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 对象字符串转为对象
|
|
14
|
+
* @param objStr
|
|
15
|
+
*/
|
|
16
|
+
export const objStrToObj = function (objStr) {
|
|
17
|
+
let _objStr = objStr;
|
|
18
|
+
if (typeof _objStr === "object") {
|
|
19
|
+
return _objStr;
|
|
20
|
+
}
|
|
21
|
+
// 对象字符串 转为 对象
|
|
22
|
+
if (typeof _objStr === "string") {
|
|
23
|
+
_objStr = _objStr.trim();
|
|
24
|
+
if (/^\[.*\]$|^\{.*\}$/.test(_objStr)) {
|
|
25
|
+
try {
|
|
26
|
+
_objStr = JSON.parse(_objStr);
|
|
27
|
+
} catch (error) {}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return _objStr;
|
|
31
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
|
|
1
3
|
import { axios } from "@hzab/data-model";
|
|
2
4
|
|
|
3
5
|
import { formatDirStr, mergeDirStr, getFileNameByFileObj } from "./uploadUtils";
|
|
@@ -102,7 +104,7 @@ export function getSignature(opt: IGetSignatureOpt = {}) {
|
|
|
102
104
|
window._$offlineSignatureRes &&
|
|
103
105
|
window._$offlineSignatureRes.data &&
|
|
104
106
|
serverUrl + JSON.stringify(params) === window._$offlineSignatureRes.serverUrlParams &&
|
|
105
|
-
|
|
107
|
+
dayjs().valueOf() - window._$offlineSignatureRes.__saveTime < window._$offlineSignatureRes.expireTimeMilles - 10000
|
|
106
108
|
) {
|
|
107
109
|
return Promise.resolve(window._$offlineSignatureRes.data);
|
|
108
110
|
}
|
|
@@ -128,7 +130,7 @@ export function getSignature(opt: IGetSignatureOpt = {}) {
|
|
|
128
130
|
data: res?.data?.data,
|
|
129
131
|
};
|
|
130
132
|
if (window._$offlineSignatureRes) {
|
|
131
|
-
window._$offlineSignatureRes.__saveTime =
|
|
133
|
+
window._$offlineSignatureRes.__saveTime = dayjs().valueOf();
|
|
132
134
|
window._$offlineSignatureRes.serverUrlParams = serverUrl + JSON.stringify(params);
|
|
133
135
|
}
|
|
134
136
|
return window._$offlineSignatureRes.data;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 文件数据格式归一化
|
|
3
|
+
*/
|
|
4
|
+
import dayjs from "dayjs";
|
|
5
|
+
|
|
6
|
+
import { nanoidNumALetters } from "../nanoid";
|
|
7
|
+
import { objStrToObj } from "../string";
|
|
8
|
+
import { isBase64Str, getFileTypeFromBase64 } from "../base64Str";
|
|
9
|
+
import { getFileURL } from "../file/file";
|
|
10
|
+
import { getFileType } from "../file/fileType";
|
|
11
|
+
import { getUFileName, getFileName, getFullFileName, getFileExt } from "../file/fileName";
|
|
12
|
+
import { getFileNameObj, mergePreviewConfig } from "./uploadUtils";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 文件数据格式
|
|
16
|
+
*/
|
|
17
|
+
export interface IFile {
|
|
18
|
+
/** 唯一 id */
|
|
19
|
+
uid?: string;
|
|
20
|
+
/** 唯一 id */
|
|
21
|
+
id: string;
|
|
22
|
+
/** 文件地址 */
|
|
23
|
+
url: string;
|
|
24
|
+
/** 存储的文件地址 */
|
|
25
|
+
storeUrl: string;
|
|
26
|
+
/** 存储的文件名称 */
|
|
27
|
+
name?: string;
|
|
28
|
+
/** 存储的文件名称 */
|
|
29
|
+
filename?: string;
|
|
30
|
+
/** 上传的文件名称 */
|
|
31
|
+
originalFilename: string;
|
|
32
|
+
/** 基础路径 */
|
|
33
|
+
basePath?: string;
|
|
34
|
+
/** 存储路径 */
|
|
35
|
+
path?: string;
|
|
36
|
+
/** 文件后缀 */
|
|
37
|
+
ext?: string;
|
|
38
|
+
/** 文件类型 */
|
|
39
|
+
contentType: string;
|
|
40
|
+
/** 上传平台 */
|
|
41
|
+
platform: string;
|
|
42
|
+
/** 文件预览地址 */
|
|
43
|
+
previewUrl?: string;
|
|
44
|
+
/** 文件缩略图地址 */
|
|
45
|
+
thumbnailUrl?: string;
|
|
46
|
+
/** 文件类型 */
|
|
47
|
+
type: string;
|
|
48
|
+
/** 文件大小 */
|
|
49
|
+
size: number;
|
|
50
|
+
/** 原始 file 文件 */
|
|
51
|
+
originFileObj?: File;
|
|
52
|
+
/** 文件上传时间 */
|
|
53
|
+
createTime?: string;
|
|
54
|
+
/** 最后更新时间戳 */
|
|
55
|
+
lastModified?: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 数据处理配置项
|
|
60
|
+
*/
|
|
61
|
+
export interface INormalizationOpt {
|
|
62
|
+
/** 列表数据模式:数组 | 字符串 */
|
|
63
|
+
listMode: "array" | "jsonStr" | "splitStr";
|
|
64
|
+
/** 字符串列表数据模式下的分隔符 */
|
|
65
|
+
split: string;
|
|
66
|
+
/** 子项数据模式: 格式化对象 | file 文件格式 | url 地址 | JSON 序列化字符串 */
|
|
67
|
+
itemMode: "object" | "file" | "url" | "jsonStr";
|
|
68
|
+
/** 预览配置参数 */
|
|
69
|
+
previewConfig?: Object;
|
|
70
|
+
/** 设置预览 URL 的回调 */
|
|
71
|
+
getPreviewUrl?: (file, opt) => string;
|
|
72
|
+
/** 缩略图配置参数 */
|
|
73
|
+
thumbnailUrlConf?: Object;
|
|
74
|
+
/** 设置缩略图 URL 的回调 */
|
|
75
|
+
getThumbnailUrl?: (file, opt) => string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 内部文件格式 */
|
|
79
|
+
const fileTpl: IFile = {
|
|
80
|
+
uid: "",
|
|
81
|
+
id: "",
|
|
82
|
+
url: "",
|
|
83
|
+
storeUrl: "",
|
|
84
|
+
name: "",
|
|
85
|
+
filename: "",
|
|
86
|
+
originalFilename: "",
|
|
87
|
+
basePath: "",
|
|
88
|
+
path: "",
|
|
89
|
+
ext: "",
|
|
90
|
+
type: "",
|
|
91
|
+
contentType: "",
|
|
92
|
+
previewUrl: undefined,
|
|
93
|
+
size: undefined,
|
|
94
|
+
platform: undefined,
|
|
95
|
+
lastModified: undefined,
|
|
96
|
+
createTime: undefined,
|
|
97
|
+
originFileObj: undefined,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 文件数据格式归一化
|
|
102
|
+
*/
|
|
103
|
+
export const normalizationFileObj = (data: IFile, opt: INormalizationOpt) => {
|
|
104
|
+
if (!data) {
|
|
105
|
+
console.warn("Upload handleInputData 请传入正确的数据");
|
|
106
|
+
return data;
|
|
107
|
+
}
|
|
108
|
+
let file = {
|
|
109
|
+
...fileTpl,
|
|
110
|
+
};
|
|
111
|
+
// 对象字符串 转为 对象
|
|
112
|
+
const _data = objStrToObj(data);
|
|
113
|
+
if (isBase64Str(_data)) {
|
|
114
|
+
// base64 格式
|
|
115
|
+
file.id = nanoidNumALetters();
|
|
116
|
+
file.uid = file.id;
|
|
117
|
+
file.url = _data;
|
|
118
|
+
file.storeUrl = _data;
|
|
119
|
+
file.name = file.id;
|
|
120
|
+
file.filename = file.name;
|
|
121
|
+
file.originalFilename = file.name;
|
|
122
|
+
// 根据 base64 数据获取文件类型
|
|
123
|
+
const typeObj = getFileTypeFromBase64(_data);
|
|
124
|
+
file.type = typeObj.mimeType;
|
|
125
|
+
file.ext = typeObj.type;
|
|
126
|
+
} else if (typeof _data === "string") {
|
|
127
|
+
// url 格式
|
|
128
|
+
const obj = getFileNameObj(_data);
|
|
129
|
+
file.id = obj?.id;
|
|
130
|
+
file.uid = file.id;
|
|
131
|
+
file.url = _data;
|
|
132
|
+
file.storeUrl = _data;
|
|
133
|
+
file.name = getFullFileName(file.url);
|
|
134
|
+
file.filename = file.name;
|
|
135
|
+
file.originalFilename = getFullFileName(file.url);
|
|
136
|
+
file.type = getFileType(file.url);
|
|
137
|
+
file.ext = getFileExt(file.url);
|
|
138
|
+
} else if (typeof _data === "object") {
|
|
139
|
+
const sourceData = _data.originFileObj || _data;
|
|
140
|
+
file.id = _data.id || _data.uid;
|
|
141
|
+
file.url = sourceData.url || sourceData.ossUrl;
|
|
142
|
+
file.storeUrl = file.url;
|
|
143
|
+
file.originalFilename = _data.originalFilename || getFullFileName(file.url) || _data.name;
|
|
144
|
+
file.name = _data.filename || _data.name || getFullFileName(file.url);
|
|
145
|
+
file.filename = file.name;
|
|
146
|
+
file.originFileObj = data instanceof File ? data : undefined;
|
|
147
|
+
file.lastModified = _data.lastModified;
|
|
148
|
+
file.basePath = _data.basePath;
|
|
149
|
+
file.path = _data.path;
|
|
150
|
+
file.ext = getFileExt(file.url);
|
|
151
|
+
file.type = _data.contentType || _data.type;
|
|
152
|
+
file.contentType = file.type;
|
|
153
|
+
file.size = _data.size;
|
|
154
|
+
file.platform = _data.platform;
|
|
155
|
+
file.createTime = _data.createTime;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!file.id) {
|
|
159
|
+
file.id =
|
|
160
|
+
file.uid ||
|
|
161
|
+
`${
|
|
162
|
+
getFileName(file.url || file.name || file.filename) || "rc-upload"
|
|
163
|
+
}-${dayjs().valueOf()}-${nanoidNumALetters()}`;
|
|
164
|
+
}
|
|
165
|
+
if (!file.name) {
|
|
166
|
+
file.name = file.id;
|
|
167
|
+
}
|
|
168
|
+
if (!file.filename) {
|
|
169
|
+
file.filename = file.name;
|
|
170
|
+
}
|
|
171
|
+
if (!file.storeUrl) {
|
|
172
|
+
file.storeUrl = file.url;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 预览地址
|
|
176
|
+
if (!file.previewUrl) {
|
|
177
|
+
const { getPreviewUrl, previewConfig } = opt || {};
|
|
178
|
+
file.previewUrl = getPreviewUrl ? getPreviewUrl(file, opt) : mergePreviewConfig(file, previewConfig);
|
|
179
|
+
}
|
|
180
|
+
// 缩略图地址
|
|
181
|
+
if (!file.thumbnailUrl) {
|
|
182
|
+
const { getThumbnailUrl, thumbnailUrlConf } = opt || {};
|
|
183
|
+
file.thumbnailUrl = getThumbnailUrl ? getThumbnailUrl(file, opt) : mergePreviewConfig(file, thumbnailUrlConf);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return file;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 解析传入的文件列表数据
|
|
191
|
+
*/
|
|
192
|
+
export const parseFileList = (fileList, opt: INormalizationOpt) => {
|
|
193
|
+
if (!fileList) {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let _fileList = fileList;
|
|
198
|
+
const { listMode, split } = opt || {};
|
|
199
|
+
|
|
200
|
+
// splitStr jsonStr 模式下处理字符串
|
|
201
|
+
const isStr = typeof _fileList === "string";
|
|
202
|
+
if (isStr && listMode === "splitStr") {
|
|
203
|
+
// @ts-ignore
|
|
204
|
+
_fileList = _fileList?.split(split ?? ", ");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (isStr && listMode === "jsonStr") {
|
|
208
|
+
_fileList = objStrToObj(_fileList);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const isArr = Array.isArray(_fileList);
|
|
212
|
+
// 统一成数组格式
|
|
213
|
+
if (_fileList !== null && _fileList !== undefined && !isArr) {
|
|
214
|
+
_fileList = [_fileList];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (isArr) {
|
|
218
|
+
const arrRes = [];
|
|
219
|
+
_fileList.forEach((it) => {
|
|
220
|
+
const itRes = normalizationFileObj(it, opt);
|
|
221
|
+
itRes && arrRes.push(itRes);
|
|
222
|
+
});
|
|
223
|
+
return arrRes;
|
|
224
|
+
}
|
|
225
|
+
return _fileList;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* 出参子项归一化
|
|
230
|
+
* 根据配置,将 对象 处理成所需格式
|
|
231
|
+
*/
|
|
232
|
+
export const formatFile = function (file, opt: INormalizationOpt) {
|
|
233
|
+
const { itemMode = "object" } = opt || {};
|
|
234
|
+
if (itemMode === "file") {
|
|
235
|
+
return file?.originFileObj || file;
|
|
236
|
+
}
|
|
237
|
+
if (itemMode === "object") {
|
|
238
|
+
return normalizationFileObj(file, opt);
|
|
239
|
+
}
|
|
240
|
+
if (itemMode === "jsonStr" && typeof file !== "string") {
|
|
241
|
+
const { originFileObj, ...jFile } = normalizationFileObj(file, opt);
|
|
242
|
+
return JSON.stringify({ ...jFile });
|
|
243
|
+
}
|
|
244
|
+
if (itemMode === "url") {
|
|
245
|
+
return typeof file === "string" ? file : file.originFileObj?.url || file?.url;
|
|
246
|
+
}
|
|
247
|
+
return file;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 出参 fileList 归一化
|
|
252
|
+
* 根据配置,将 对象 处理成所需格式
|
|
253
|
+
*/
|
|
254
|
+
export const formatFileList = function (fileList, opt: INormalizationOpt) {
|
|
255
|
+
if (!fileList) {
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const { listMode, split } = opt || {};
|
|
260
|
+
|
|
261
|
+
let _fileList = fileList;
|
|
262
|
+
if (_fileList && !Array.isArray(_fileList)) {
|
|
263
|
+
_fileList = [fileList];
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 数组
|
|
267
|
+
if (listMode === "array") {
|
|
268
|
+
return _fileList?.map((it) => formatFile(it, opt));
|
|
269
|
+
}
|
|
270
|
+
// 序列化后的字符串
|
|
271
|
+
if (listMode === "jsonStr") {
|
|
272
|
+
return JSON.stringify(_fileList?.map((it) => formatFile(it, opt)));
|
|
273
|
+
}
|
|
274
|
+
// join 后的字符串
|
|
275
|
+
if (listMode === "splitStr") {
|
|
276
|
+
return _fileList
|
|
277
|
+
?.map((it) => formatFile(it, opt))
|
|
278
|
+
.filter((it) => it)
|
|
279
|
+
.join(split ?? ", ");
|
|
280
|
+
}
|
|
281
|
+
};
|
package/src/upload/ossUpload.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
|
|
1
3
|
import { axios } from "@hzab/data-model";
|
|
2
4
|
import { formatDirStr, mergeDirStr, getFileNameByFileObj } from "./uploadUtils";
|
|
3
5
|
|
|
@@ -65,7 +67,7 @@ export function getSignature(opt: IGetSignatureOpt = {}) {
|
|
|
65
67
|
if (
|
|
66
68
|
window._$ossSignatureRes &&
|
|
67
69
|
serverUrl === window._$ossSignatureRes.serverUrl &&
|
|
68
|
-
|
|
70
|
+
dayjs().valueOf() - window._$ossSignatureRes.__saveTime < window._$ossSignatureRes.expireTimeMilles - 10000
|
|
69
71
|
) {
|
|
70
72
|
return Promise.resolve(window._$ossSignatureRes);
|
|
71
73
|
}
|
|
@@ -84,7 +86,7 @@ export function getSignature(opt: IGetSignatureOpt = {}) {
|
|
|
84
86
|
.then((res) => {
|
|
85
87
|
window._$ossSignatureRes = res?.data?.data ?? res?.data ?? res;
|
|
86
88
|
if (window._$ossSignatureRes) {
|
|
87
|
-
window._$ossSignatureRes.__saveTime =
|
|
89
|
+
window._$ossSignatureRes.__saveTime = dayjs().valueOf();
|
|
88
90
|
window._$ossSignatureRes.serverUrl = serverUrl;
|
|
89
91
|
}
|
|
90
92
|
return window._$ossSignatureRes;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
|
|
1
3
|
import { getArr } from "../array";
|
|
4
|
+
import { isBase64Str } from "../base64Str";
|
|
2
5
|
|
|
6
|
+
import { mergeFileName, getFileName } from "../file/fileName";
|
|
3
7
|
import { nanoidNumALetters } from "../nanoid";
|
|
4
|
-
import { mergeFileName, getFullFileName } from "../file/fileName";
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* 格式化 dir 字符串,必须为非 / 开头, / 结尾。如: test/
|
|
@@ -51,6 +54,30 @@ export function getPreviewUrl(uri, previewConfig) {
|
|
|
51
54
|
return uri;
|
|
52
55
|
}
|
|
53
56
|
|
|
57
|
+
|
|
58
|
+
export const getFileNameExt = function (fileName,) {
|
|
59
|
+
const regex = /^(.*)\.([^.]+)$/;
|
|
60
|
+
const result = fileName.match(regex);
|
|
61
|
+
if (result) {
|
|
62
|
+
const [, mainPart, ext] = result;
|
|
63
|
+
return {
|
|
64
|
+
mainPart,
|
|
65
|
+
ext
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
mainPart: fileName,
|
|
70
|
+
ext: ""
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const getFileTypeExt = function (type,) {
|
|
75
|
+
if (type.indexOf("/") >= 0) {
|
|
76
|
+
return type?.match(/[^\/]*\/([^\/]+)$/)?.[1]
|
|
77
|
+
};
|
|
78
|
+
return type;
|
|
79
|
+
};
|
|
80
|
+
|
|
54
81
|
/**
|
|
55
82
|
* 根据 file 对象获取文件名称,文件名称包含:初始名称、类型
|
|
56
83
|
* 数据存储格式 ~k[key]-[data]~ 如: ~ktime-1743649562530~
|
|
@@ -58,22 +85,31 @@ export function getPreviewUrl(uri, previewConfig) {
|
|
|
58
85
|
* @param file
|
|
59
86
|
*/
|
|
60
87
|
export const getFileNameByFileObj = (file, opt: any) => {
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
88
|
+
|
|
89
|
+
const {
|
|
90
|
+
useHashName = true,
|
|
91
|
+
params: { fileName = "" } = {} // 给 params 加默认空对象
|
|
92
|
+
} = opt || {};
|
|
93
|
+
|
|
94
|
+
const { mainPart,
|
|
95
|
+
ext } = getFileNameExt(file.name || "");
|
|
96
|
+
|
|
97
|
+
const type = ext || getFileTypeExt(file.type || file.contentType);
|
|
98
|
+
|
|
99
|
+
const _fileName = fileName || mainPart || getFileName(file.url) || "oss";
|
|
100
|
+
|
|
101
|
+
if (_fileName.indexOf("~kid-") >= 0) {
|
|
64
102
|
return _fileName;
|
|
65
103
|
}
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}),
|
|
76
|
-
);
|
|
104
|
+
|
|
105
|
+
const objStr = setFileNameObj({
|
|
106
|
+
t: file.createTime || dayjs().valueOf(),
|
|
107
|
+
ext: file.ext || (file.type || file.contentType)?.replace("/", "_"),
|
|
108
|
+
});
|
|
109
|
+
const hashStr = useHashName ? `${nanoidNumALetters()}-` : "";
|
|
110
|
+
const nm = mergeFileName(_fileName + "." + type, `~kid-${hashStr}${objStr}`);
|
|
111
|
+
|
|
112
|
+
return nm
|
|
77
113
|
};
|
|
78
114
|
|
|
79
115
|
/**
|
|
@@ -116,3 +152,40 @@ export const getValByKey = function (url, key) {
|
|
|
116
152
|
const reg = new RegExp(`~k${key?.replace(/~/g, "_")}-\([^~]*\)~`);
|
|
117
153
|
return url.match(reg)?.[1];
|
|
118
154
|
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 预览 url 添加前缀
|
|
158
|
+
* @param {*} uri
|
|
159
|
+
* @param {*} conf
|
|
160
|
+
* @returns
|
|
161
|
+
*/
|
|
162
|
+
export function mergePreviewConfig(uri, conf) {
|
|
163
|
+
let _uri = uri;
|
|
164
|
+
let basePath = "";
|
|
165
|
+
if (typeof _uri === "object") {
|
|
166
|
+
basePath = _uri.basePath;
|
|
167
|
+
_uri = _uri.previewUrl || _uri.url;
|
|
168
|
+
}
|
|
169
|
+
if (typeof _uri !== "string") {
|
|
170
|
+
return _uri;
|
|
171
|
+
}
|
|
172
|
+
_uri = _uri?.trim();
|
|
173
|
+
// base64 直接返回
|
|
174
|
+
if (isBase64Str(_uri)) {
|
|
175
|
+
return _uri;
|
|
176
|
+
}
|
|
177
|
+
const isUriPattern = /http[s]?:\/\/[^\s]+/.test(_uri);
|
|
178
|
+
const query = conf?.query?.trim() ? "?" + conf?.query?.trim() : "";
|
|
179
|
+
|
|
180
|
+
if (isUriPattern) {
|
|
181
|
+
// 源地址为 http 地址,直接拼接 query 参数
|
|
182
|
+
return `${_uri}${query}`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (conf) {
|
|
186
|
+
// 拼接前缀
|
|
187
|
+
return `${conf?.url?.trim()?.replace(/\/$/, "")}/${_uri?.trim()?.replace(/^\//, "")}${query}`?.trim();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return _uri;
|
|
191
|
+
}
|