@haluo/util 2.0.23 → 2.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -1
- package/dist/index.js +33 -61
- package/dist/modules/cookie/index.js +18 -24
- package/dist/modules/date/index.js +49 -56
- package/dist/modules/dom/index.js +16 -23
- package/dist/modules/filter/index.js +15 -25
- package/dist/modules/format/index.js +6 -12
- package/dist/modules/match/index.js +6 -10
- package/dist/modules/monitor/index.js +7 -14
- package/dist/modules/monitor/lib/jsError.js +39 -35
- package/dist/modules/monitor/lib/timing.js +24 -28
- package/dist/modules/monitor/lib/xhr.js +26 -33
- package/dist/modules/monitor/utils/onload.js +1 -4
- package/dist/modules/monitor/utils/tracker.js +13 -27
- package/dist/modules/number/index.js +31 -35
- package/dist/modules/open-app/index.js +66 -61
- package/dist/modules/sentry/index.js +19 -27
- package/dist/modules/tools/index.d.ts +1 -1
- package/dist/modules/tools/index.js +158 -169
- package/dist/modules/upload/aliOss.d.ts +75 -124
- package/dist/modules/upload/aliOss.js +175 -357
- package/dist/modules/upload/index.d.ts +2 -14
- package/dist/modules/upload/index.js +2 -6
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/index.js +1 -2
- package/package.json +14 -3
- package/dist/modules/upload/aliOss copy.d.ts +0 -321
- package/dist/modules/upload/aliOss copy.js +0 -734
- package/dist/modules/upload/backup-aliOss.d.ts +0 -321
- package/dist/modules/upload/backup-aliOss.js +0 -734
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file 阿里云OSS上传统一工具类
|
|
2
|
+
* @file 阿里云OSS上传统一工具类 v4
|
|
3
3
|
* @description 整合所有项目的aliOss业务逻辑,通过传参形式支持不同业务场景
|
|
4
4
|
* @Author: wanghui
|
|
5
5
|
* @createBy: @2025.11.17
|
|
6
6
|
*
|
|
7
|
-
* 文档:https://help.aliyun.com/document_detail/64041.html
|
|
8
|
-
*
|
|
9
7
|
* 使用示例:
|
|
10
8
|
* ```typescript
|
|
11
9
|
* import { createAliOssUploader } from '@haluo/util'
|
|
@@ -13,12 +11,9 @@
|
|
|
13
11
|
* // 创建上传实例,传入业务相关的API函数
|
|
14
12
|
* const ossUploader = createAliOssUploader({
|
|
15
13
|
* getSts: (params) => api.getSts(params),
|
|
16
|
-
* getAliyunSts: (params) => api.getAliyunSts(params),
|
|
17
14
|
* darkWaterUploadImage: (params) => api.darkWaterUploadImage(params),
|
|
18
15
|
* multiTransferImage: (params) => api.multiTransferImage(params),
|
|
19
|
-
* aliyunPersist: (params) => api.aliyunPersist(params),
|
|
20
16
|
* generatePrePresignedUrl: (params) => api.generatePrePresignedUrl(params),
|
|
21
|
-
* uploadConf: (params) => api.uploadConf(params),
|
|
22
17
|
* dateFormat: util.date.format, // 或 util.filter.format
|
|
23
18
|
* messageWarning: (msg) => window.$message.warning(msg)
|
|
24
19
|
* })
|
|
@@ -26,9 +21,9 @@
|
|
|
26
21
|
* // 图片上传
|
|
27
22
|
* ossUploader.ossUploadImage({
|
|
28
23
|
* file: file,
|
|
24
|
+
* businessType: 1500, // 业务类型 BusinessType
|
|
29
25
|
* imageType: 'official', // nowater、official、panoram、forum、avatar、square、carport
|
|
30
26
|
* quality: 0.7, // 压缩质量
|
|
31
|
-
* businessType: 1500, // 业务类型:0(无水印)、1(头像)、4(GIF)、6(视频)、8(UnProtect)、1000(文章)、1199(私有)、1299(加密)、1500(默认)
|
|
32
27
|
* batchTransfer: false, // 是否批量转换水印
|
|
33
28
|
* notCompress: false, // 是否不压缩
|
|
34
29
|
* idCard: false, // 是否身份证上传
|
|
@@ -40,25 +35,38 @@
|
|
|
40
35
|
* // 文件上传
|
|
41
36
|
* ossUploader.ossUploadFile({
|
|
42
37
|
* file: file,
|
|
38
|
+
* businessType: 1500, // 业务类型 BusinessType
|
|
43
39
|
* isVideo: false, // 是否视频
|
|
44
40
|
* isUnProtect: false, // 是否UnProtect
|
|
45
41
|
* isDocument: false, // 是否文档
|
|
46
|
-
* businessType: 1500,
|
|
47
42
|
* onProgress: (e) => console.log(e.percent),
|
|
48
43
|
* onSuccess: (val) => console.log(val),
|
|
49
44
|
* onError: (err) => console.error(err)
|
|
50
45
|
* })
|
|
51
46
|
* ```
|
|
52
47
|
*/
|
|
53
|
-
import OSS from 'ali-oss';
|
|
54
48
|
import lrz from 'lrz';
|
|
55
49
|
// ==================== 常量定义 ====================
|
|
56
50
|
/** 支持的图片格式 */
|
|
57
51
|
const SUPPORTED_IMAGE_TYPES = [
|
|
52
|
+
'image/jpeg',
|
|
58
53
|
'image/png',
|
|
59
54
|
'image/gif',
|
|
60
|
-
'image/
|
|
61
|
-
'image/
|
|
55
|
+
'image/bmp',
|
|
56
|
+
'image/webp',
|
|
57
|
+
'image/svg+xml',
|
|
58
|
+
'image/tiff',
|
|
59
|
+
];
|
|
60
|
+
/** 支持的图片格式 ext */
|
|
61
|
+
const SUPPORTED_IMAGE_EXTS = [
|
|
62
|
+
'jpg',
|
|
63
|
+
'jpeg',
|
|
64
|
+
'png',
|
|
65
|
+
'gif',
|
|
66
|
+
'bmp',
|
|
67
|
+
'webp',
|
|
68
|
+
'svg',
|
|
69
|
+
'tiff',
|
|
62
70
|
];
|
|
63
71
|
/** 小图片阈值(KB) */
|
|
64
72
|
const SMALL_IMAGE_THRESHOLD = 100;
|
|
@@ -68,51 +76,73 @@ const LONG_IMAGE_RATIO = 2;
|
|
|
68
76
|
const DEFAULT_FILE_SIZE_LIMIT = 10;
|
|
69
77
|
/** 默认压缩质量 */
|
|
70
78
|
const DEFAULT_QUALITY = 0.7;
|
|
71
|
-
/** 默认图片类型 */
|
|
72
|
-
const DEFAULT_IMAGE_TYPE = 'official';
|
|
73
|
-
/** OSS默认区域 */
|
|
74
|
-
const DEFAULT_OSS_REGION = 'oss-cn-beijing';
|
|
75
79
|
/** 预签名URL过期时间(毫秒) */
|
|
76
80
|
const PRESIGNED_URL_EXPIRE_TIME = 60000;
|
|
77
|
-
/** 随机字符串字符集 */
|
|
78
|
-
const RANDOM_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz';
|
|
79
|
-
/** OSS元数据 */
|
|
80
|
-
const OSS_META_PEOPLE = 'x-oss-meta-motor';
|
|
81
81
|
// ==================== 枚举定义 ====================
|
|
82
82
|
/**
|
|
83
83
|
* 业务类型枚举
|
|
84
|
-
* 不同的businessType对应不同的OSS bucket和水印策略
|
|
85
84
|
*/
|
|
86
85
|
export var BusinessType;
|
|
87
86
|
(function (BusinessType) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// GIF = 4, // GIF图片
|
|
91
|
-
// VIDEO = 6, // 视频
|
|
92
|
-
// UNPROTECT = 8, // UnProtect(不受保护)
|
|
93
|
-
// ARTICLE = 1000, // 文章图片
|
|
94
|
-
// PRIVATE = 1199, // 私有
|
|
95
|
-
// ENCRYPTED = 1299, // 加密
|
|
96
|
-
// DEFAULT = 1500, // 默认
|
|
87
|
+
/** 客户端上传图片(跟用户相关) */
|
|
88
|
+
/** 隐私图片 */
|
|
97
89
|
BusinessType[BusinessType["CREDIT"] = 130541736] = "CREDIT";
|
|
90
|
+
/** 内容 */
|
|
98
91
|
BusinessType[BusinessType["CONTENT"] = 101300784] = "CONTENT";
|
|
92
|
+
/** 头像 */
|
|
99
93
|
BusinessType[BusinessType["AVATAR"] = 134539448] = "AVATAR";
|
|
94
|
+
/** 二手车 */
|
|
100
95
|
BusinessType[BusinessType["USED_CAR"] = 171978237] = "USED_CAR";
|
|
96
|
+
/** 商城 */
|
|
101
97
|
BusinessType[BusinessType["MALL"] = 115259415] = "MALL";
|
|
98
|
+
/** 魔友圈 */
|
|
102
99
|
BusinessType[BusinessType["HOOP"] = 155669648] = "HOOP";
|
|
100
|
+
/** 用户 */
|
|
103
101
|
BusinessType[BusinessType["USER"] = 170244368] = "USER";
|
|
102
|
+
/** 骑行 */
|
|
104
103
|
BusinessType[BusinessType["CYCLING"] = 165103952] = "CYCLING";
|
|
104
|
+
/** 玩车 */
|
|
105
105
|
BusinessType[BusinessType["MOTORCYCLE"] = 151637735] = "MOTORCYCLE";
|
|
106
|
+
/** 租车 */
|
|
106
107
|
BusinessType[BusinessType["RENTALCAR"] = 149095014] = "RENTALCAR";
|
|
108
|
+
/** 经销商 */
|
|
107
109
|
BusinessType[BusinessType["SHOP"] = 172655993] = "SHOP";
|
|
110
|
+
/** 财务 */
|
|
108
111
|
BusinessType[BusinessType["FINANCE"] = 188717541] = "FINANCE";
|
|
112
|
+
/** 反馈 */
|
|
109
113
|
BusinessType[BusinessType["FEEDBACK"] = 182411383] = "FEEDBACK";
|
|
114
|
+
/** 驾校 */
|
|
110
115
|
BusinessType[BusinessType["DRIVING_SCHOOL"] = 172137757] = "DRIVING_SCHOOL";
|
|
116
|
+
/** 评论图片 */
|
|
111
117
|
BusinessType[BusinessType["REPLY"] = 195929698] = "REPLY";
|
|
118
|
+
/** 厂家 */
|
|
112
119
|
BusinessType[BusinessType["FACTORY"] = 118733601] = "FACTORY";
|
|
120
|
+
/** 骑行数据 */
|
|
113
121
|
BusinessType[BusinessType["CYCLING_DATA"] = 148681294] = "CYCLING_DATA";
|
|
122
|
+
/** 动图 */
|
|
114
123
|
BusinessType[BusinessType["GIF"] = 191290831] = "GIF";
|
|
124
|
+
/** 其他(原则上不可以使用) */
|
|
115
125
|
BusinessType[BusinessType["OTHER"] = 100694193] = "OTHER";
|
|
126
|
+
/** 运营文件(跟用户无关) */
|
|
127
|
+
/** apk */
|
|
128
|
+
BusinessType[BusinessType["APK"] = 154033062] = "APK";
|
|
129
|
+
/** 证书 */
|
|
130
|
+
BusinessType[BusinessType["CERT"] = 142668067] = "CERT";
|
|
131
|
+
/** oss数据文件(xlxs等) */
|
|
132
|
+
BusinessType[BusinessType["OSSDATA"] = 105038102] = "OSSDATA";
|
|
133
|
+
/** 数据(题库等) */
|
|
134
|
+
BusinessType[BusinessType["DB"] = 180241065] = "DB";
|
|
135
|
+
/** PDF */
|
|
136
|
+
BusinessType[BusinessType["PDF"] = 198157532] = "PDF";
|
|
137
|
+
/** 视频 */
|
|
138
|
+
BusinessType[BusinessType["VIDEOOPS"] = 172023381] = "VIDEOOPS";
|
|
139
|
+
/** 运营图片(跟用户无关) */
|
|
140
|
+
/** 商城运营图片 */
|
|
141
|
+
BusinessType[BusinessType["MALLOPS"] = 137563987] = "MALLOPS";
|
|
142
|
+
/** 运营图片 */
|
|
143
|
+
BusinessType[BusinessType["OSSOPS"] = 157532694] = "OSSOPS";
|
|
144
|
+
/** 车库运营图片 */
|
|
145
|
+
BusinessType[BusinessType["CARPORT"] = 137563987] = "CARPORT";
|
|
116
146
|
})(BusinessType || (BusinessType = {}));
|
|
117
147
|
/**
|
|
118
148
|
* bucket 图片类型后缀枚举
|
|
@@ -126,44 +156,7 @@ export const SuffixEnum = {
|
|
|
126
156
|
square: '!square',
|
|
127
157
|
carport: '!carport', // 车库、经销商水印
|
|
128
158
|
};
|
|
129
|
-
/** 不打水印的图片类型 */
|
|
130
|
-
const NO_WATER_IMAGE_TYPES = ['nowater', 'forum'];
|
|
131
159
|
// ==================== 工具函数 ====================
|
|
132
|
-
/**
|
|
133
|
-
* 生成随机字符串
|
|
134
|
-
* @param length 字符串长度
|
|
135
|
-
* @returns 随机字符串
|
|
136
|
-
*/
|
|
137
|
-
function randomString(length) {
|
|
138
|
-
let result = '';
|
|
139
|
-
for (let i = 0; i < length; i++) {
|
|
140
|
-
const randomIndex = Math.floor(Math.random() * RANDOM_CHARS.length);
|
|
141
|
-
result += RANDOM_CHARS[randomIndex];
|
|
142
|
-
}
|
|
143
|
-
return result;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* 过滤对象中的undefined值
|
|
147
|
-
* @param obj 原始对象
|
|
148
|
-
* @returns 过滤后的对象
|
|
149
|
-
*/
|
|
150
|
-
function filterUndefined(obj) {
|
|
151
|
-
const result = {};
|
|
152
|
-
Object.keys(obj).forEach((key) => {
|
|
153
|
-
if (obj[key] !== undefined) {
|
|
154
|
-
result[key] = obj[key];
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
return result;
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* 检查是否为不打水印的图片类型
|
|
161
|
-
* @param imageType 图片类型
|
|
162
|
-
* @returns 是否不打水印
|
|
163
|
-
*/
|
|
164
|
-
function isNoWaterImageType(imageType) {
|
|
165
|
-
return NO_WATER_IMAGE_TYPES.includes(imageType);
|
|
166
|
-
}
|
|
167
160
|
/**
|
|
168
161
|
* 检查文件类型是否支持
|
|
169
162
|
* @param fileType 文件类型
|
|
@@ -195,36 +188,58 @@ function getFileSizeInMB(size) {
|
|
|
195
188
|
*/
|
|
196
189
|
export class AliOssClass {
|
|
197
190
|
apiConfig;
|
|
198
|
-
clientCache;
|
|
199
191
|
constructor(apiConfig) {
|
|
200
192
|
this.apiConfig = apiConfig;
|
|
201
|
-
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* 判断文件是否为图片(支持常见图片格式)
|
|
196
|
+
* @param {File} file - 上传的File对象
|
|
197
|
+
* @returns {boolean} 是否为图片
|
|
198
|
+
*/
|
|
199
|
+
isImageFile(file) {
|
|
200
|
+
if (!file || !(file instanceof File))
|
|
201
|
+
return false;
|
|
202
|
+
// 1. 校验文件MIME类型(优先,更可靠)
|
|
203
|
+
if (SUPPORTED_IMAGE_TYPES.includes(file.type))
|
|
204
|
+
return true;
|
|
205
|
+
// 2. 校验文件后缀名(兜底,防止MIME类型缺失)
|
|
206
|
+
const fileExt = file.name.split('.').pop()?.toLowerCase() || '';
|
|
207
|
+
return SUPPORTED_IMAGE_EXTS.includes(fileExt);
|
|
202
208
|
}
|
|
203
209
|
/**
|
|
204
210
|
* 通过V4签名上传
|
|
205
211
|
* @param file 上传的文件
|
|
206
212
|
* @param businessType 业务类型
|
|
207
213
|
* @param size 文件的尺寸
|
|
214
|
+
* @param resolve Promse
|
|
215
|
+
* @param reject Promse
|
|
208
216
|
* @returns 上传图片结果
|
|
209
217
|
*/
|
|
210
|
-
async upload(
|
|
218
|
+
async upload(params) {
|
|
219
|
+
let result = {};
|
|
211
220
|
try {
|
|
212
|
-
|
|
221
|
+
if (!params.size && this.isImageFile(params.file)) {
|
|
222
|
+
const image = await this.getImageInfo(params.file);
|
|
223
|
+
params.size = `_${image?.width}_${image?.height}`;
|
|
224
|
+
}
|
|
225
|
+
// 步骤 1:请求后端获取所有签名字段
|
|
213
226
|
const signResponse = await this.apiConfig.getSts({
|
|
214
|
-
businessType,
|
|
215
|
-
docType: file.type,
|
|
216
|
-
size, // _1024_567(可选)
|
|
227
|
+
businessType: params.businessType,
|
|
228
|
+
docType: params.file.type,
|
|
229
|
+
size: params.size, // _1024_567(可选)
|
|
217
230
|
});
|
|
218
231
|
if (signResponse.data.code !== 0) {
|
|
219
|
-
|
|
220
|
-
|
|
232
|
+
const errorMessage = `获取签名失败:${signResponse.data.message}`;
|
|
233
|
+
params.callbacks?.onError(errorMessage);
|
|
234
|
+
params.reject && params.reject(errorMessage);
|
|
235
|
+
console.error(errorMessage);
|
|
221
236
|
}
|
|
222
237
|
const signData = await signResponse.data.data;
|
|
223
238
|
console.log('后端返回的签名字段:', signData);
|
|
224
|
-
//
|
|
239
|
+
// 步骤 2:构造 FormData(字段名必须和后端返回一致
|
|
225
240
|
const formData = new FormData();
|
|
226
|
-
formData.append('autherid', signData['autherid'])
|
|
227
|
-
formData.append('uid', signData['uid'])
|
|
241
|
+
// formData.append('autherid', signData['autherid'])
|
|
242
|
+
// formData.append('uid', signData['uid'])
|
|
228
243
|
formData.append('key', signData['key']);
|
|
229
244
|
formData.append('policy', signData['policy']);
|
|
230
245
|
formData.append('success_action_status', '200');
|
|
@@ -235,78 +250,39 @@ export class AliOssClass {
|
|
|
235
250
|
}
|
|
236
251
|
formData.append('x-oss-signature', signData['signature']);
|
|
237
252
|
formData.append('x-oss-signature-version', signData['signatureVersion']);
|
|
238
|
-
formData.append('file', file);
|
|
239
|
-
//
|
|
253
|
+
formData.append('file', params.file);
|
|
254
|
+
// 步骤 3:发送 POST 请求到 OSS
|
|
240
255
|
const uploadResponse = await fetch(signData.uploadUrl, {
|
|
241
256
|
method: 'POST',
|
|
242
257
|
body: formData,
|
|
243
258
|
// 重点:不要手动设置 Content-Type!浏览器会自动处理为 multipart/form-data 并带边界符
|
|
244
259
|
headers: { Accept: '*/*' },
|
|
245
260
|
});
|
|
246
|
-
//
|
|
261
|
+
// 步骤 4:处理上传结果
|
|
247
262
|
const responseText = await uploadResponse.text();
|
|
248
|
-
console.log('responseText', responseText);
|
|
249
263
|
if (uploadResponse.ok) {
|
|
250
|
-
|
|
264
|
+
result = {
|
|
265
|
+
imgUrl: signData['fileUrl'],
|
|
266
|
+
imgOrgUrl: signData['fileUrl'],
|
|
267
|
+
name: params.file.name,
|
|
268
|
+
fileName: params.file.name,
|
|
269
|
+
};
|
|
270
|
+
params.callbacks?.onSuccess(result);
|
|
271
|
+
params.resolve && params.resolve(result);
|
|
251
272
|
}
|
|
252
273
|
else {
|
|
253
|
-
|
|
274
|
+
const errorMessage = `上传失败(OSS 返回 ${uploadResponse.status}):${responseText}`;
|
|
275
|
+
params.callbacks?.onError(errorMessage);
|
|
276
|
+
params.reject && params.reject(errorMessage);
|
|
277
|
+
console.error(errorMessage);
|
|
254
278
|
}
|
|
255
279
|
}
|
|
256
280
|
catch (err) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* 创建OSS客户端
|
|
262
|
-
* @param businessType 业务类型
|
|
263
|
-
* @returns OSS客户端实例
|
|
264
|
-
*/
|
|
265
|
-
async createOssClient(businessType) {
|
|
266
|
-
const cacheKey = `${businessType}`;
|
|
267
|
-
// 注释:某些项目使用缓存,某些不使用。这里提供可选的缓存机制
|
|
268
|
-
// if (this.clientCache.has(cacheKey)) {
|
|
269
|
-
// return this.clientCache.get(cacheKey)
|
|
270
|
-
// }
|
|
271
|
-
const res = await this.apiConfig.getAliyunSts({ businessType });
|
|
272
|
-
if (res.data.code !== 0) {
|
|
273
|
-
throw new Error('获取OSS配置失败');
|
|
281
|
+
params.callbacks?.onError(err.message);
|
|
282
|
+
params.reject && params.reject(err.message);
|
|
283
|
+
console.error('上传异常:', err.message);
|
|
274
284
|
}
|
|
275
|
-
|
|
276
|
-
clientParams.suffix = clientParams.style || '';
|
|
277
|
-
// 过滤掉undefined值的属性,并确保必需字段存在
|
|
278
|
-
const filteredParams = filterUndefined(clientParams);
|
|
279
|
-
const client = new OSS({
|
|
280
|
-
region: DEFAULT_OSS_REGION,
|
|
281
|
-
...filteredParams,
|
|
282
|
-
});
|
|
283
|
-
// 保存客户端选项供后续使用
|
|
284
|
-
client.options = {
|
|
285
|
-
...client.options,
|
|
286
|
-
realmName: clientParams.realmName,
|
|
287
|
-
suffix: clientParams.suffix,
|
|
288
|
-
};
|
|
289
|
-
this.clientCache.set(cacheKey, client);
|
|
290
|
-
return client;
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* 执行OSS上传
|
|
294
|
-
* @param client OSS客户端
|
|
295
|
-
* @param fileName 文件名
|
|
296
|
-
* @param file 文件对象
|
|
297
|
-
* @param year 年份
|
|
298
|
-
* @param mimeType MIME类型
|
|
299
|
-
* @param onProgress 进度回调
|
|
300
|
-
* @returns 上传结果
|
|
301
|
-
*/
|
|
302
|
-
async performOssUpload(client, fileName, file, year, mimeType, onProgress) {
|
|
303
|
-
return await client.multipartUpload(fileName, file, {
|
|
304
|
-
progress: (p) => {
|
|
305
|
-
onProgress?.({ percent: Math.floor(p * 100) });
|
|
306
|
-
},
|
|
307
|
-
meta: { year, people: OSS_META_PEOPLE },
|
|
308
|
-
mime: mimeType,
|
|
309
|
-
});
|
|
285
|
+
return result;
|
|
310
286
|
}
|
|
311
287
|
/**
|
|
312
288
|
* 预加载图片获取尺寸
|
|
@@ -329,99 +305,33 @@ export class AliOssClass {
|
|
|
329
305
|
image.src = objectUrl;
|
|
330
306
|
});
|
|
331
307
|
}
|
|
332
|
-
/**
|
|
333
|
-
* 生成文件名
|
|
334
|
-
* @param imageType 图片类型
|
|
335
|
-
* @param extension 文件扩展名
|
|
336
|
-
* @returns 文件名信息
|
|
337
|
-
*/
|
|
338
|
-
generateFileName(imageType, extension) {
|
|
339
|
-
const now = new Date();
|
|
340
|
-
const year = this.apiConfig.dateFormat(now, 'YYYY');
|
|
341
|
-
const date = this.apiConfig.dateFormat(now, 'YYYYMMDD');
|
|
342
|
-
const dateTime = this.apiConfig.dateFormat(now, 'YYYYMMDDHHmmss');
|
|
343
|
-
const randomStr = randomString(4);
|
|
344
|
-
const fileName = `${imageType}/${date}/${dateTime}_${randomStr}${extension}`;
|
|
345
|
-
return { year, fileName };
|
|
346
|
-
}
|
|
347
308
|
/**
|
|
348
309
|
* 检查图片是否为长图
|
|
349
|
-
* @param
|
|
350
|
-
* @param height 图片高度
|
|
310
|
+
* @param File 图片
|
|
351
311
|
* @returns 是否为长图
|
|
352
312
|
*/
|
|
353
|
-
isLongImage(
|
|
313
|
+
async isLongImage(file) {
|
|
314
|
+
// 预加载图片获取尺寸
|
|
315
|
+
const image = await this.getImageInfo(file);
|
|
316
|
+
const { width, height } = image;
|
|
354
317
|
const maxDimension = Math.max(width, height);
|
|
355
318
|
const minDimension = Math.min(width, height);
|
|
356
319
|
return maxDimension / minDimension > LONG_IMAGE_RATIO;
|
|
357
320
|
}
|
|
358
|
-
/**
|
|
359
|
-
* 确定业务类型
|
|
360
|
-
* @param option 上传选项
|
|
361
|
-
* @param isGif 是否为GIF
|
|
362
|
-
* @returns 业务类型
|
|
363
|
-
*/
|
|
364
|
-
determineBusinessType(option, isGif) {
|
|
365
|
-
// 如果已指定businessType,直接使用
|
|
366
|
-
if (option.businessType !== undefined) {
|
|
367
|
-
return option.businessType;
|
|
368
|
-
}
|
|
369
|
-
const imageType = option.imageType || DEFAULT_IMAGE_TYPE;
|
|
370
|
-
// 注释:GIF图片使用businessType=4
|
|
371
|
-
if (isGif) {
|
|
372
|
-
return BusinessType.GIF;
|
|
373
|
-
}
|
|
374
|
-
// 注释:根据imageType确定默认businessType
|
|
375
|
-
if (imageType === 'avatar') {
|
|
376
|
-
return BusinessType.AVATAR;
|
|
377
|
-
}
|
|
378
|
-
if (isNoWaterImageType(imageType)) {
|
|
379
|
-
return BusinessType.OTHER;
|
|
380
|
-
}
|
|
381
|
-
return BusinessType.OTHER;
|
|
382
|
-
}
|
|
383
321
|
/**
|
|
384
322
|
* 加载图片并返回结果
|
|
385
323
|
* 用于标准的图片上传场景
|
|
386
324
|
* @param params 加载参数
|
|
387
325
|
*/
|
|
388
326
|
loadImage(params) {
|
|
389
|
-
const { url, val,
|
|
327
|
+
const { url, val, file, option, resolve, reject } = params;
|
|
390
328
|
let img = new Image();
|
|
391
329
|
img.src = url;
|
|
392
330
|
img.onload = function () {
|
|
393
331
|
if (!img)
|
|
394
332
|
return;
|
|
395
333
|
val.imgOrgUrl = `${url}?_${img.width}_${img.height}`;
|
|
396
|
-
|
|
397
|
-
val.imgUrl =
|
|
398
|
-
suffix === '!panoram' || isGif
|
|
399
|
-
? `${url}?_${img.width}_${img.height}`
|
|
400
|
-
: `${url}300?_${img.width}_${img.height}`;
|
|
401
|
-
val.name = file.name;
|
|
402
|
-
val.fileName = file.name;
|
|
403
|
-
option.onSuccess?.(val);
|
|
404
|
-
resolve(val);
|
|
405
|
-
img = null;
|
|
406
|
-
};
|
|
407
|
-
img.onerror = function () {
|
|
408
|
-
option.onError?.('图片加载失败');
|
|
409
|
-
reject('图片加载失败');
|
|
410
|
-
img = null;
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
/**
|
|
414
|
-
* 加载图片并返回结果(新版)
|
|
415
|
-
* 用于avatar、idCard等特殊场景,返回格式不同
|
|
416
|
-
* @param params 加载参数
|
|
417
|
-
*/
|
|
418
|
-
loadImageNew(params) {
|
|
419
|
-
const { url, val, file, option, resolve, reject } = params;
|
|
420
|
-
let img = new Image();
|
|
421
|
-
img.src = url;
|
|
422
|
-
img.onload = function () {
|
|
423
|
-
val.imgOrgUrl = url;
|
|
424
|
-
val.imgUrl = val.name;
|
|
334
|
+
val.imgUrl = val.imgOrgUrl;
|
|
425
335
|
val.name = file.name;
|
|
426
336
|
val.fileName = file.name;
|
|
427
337
|
option.onSuccess?.(val);
|
|
@@ -444,6 +354,9 @@ export class AliOssClass {
|
|
|
444
354
|
if (!(option.file instanceof File)) {
|
|
445
355
|
return Promise.reject('file is not instanceof File');
|
|
446
356
|
}
|
|
357
|
+
if (!option.businessType) {
|
|
358
|
+
return Promise.reject('businessType不能为空');
|
|
359
|
+
}
|
|
447
360
|
const file = option.file;
|
|
448
361
|
// 初始化回调函数
|
|
449
362
|
const callbacks = {
|
|
@@ -462,38 +375,18 @@ export class AliOssClass {
|
|
|
462
375
|
}
|
|
463
376
|
return new Promise(async (resolve, reject) => {
|
|
464
377
|
try {
|
|
465
|
-
const imageType = option.imageType || DEFAULT_IMAGE_TYPE;
|
|
466
|
-
const suffix = SuffixEnum[imageType] ||
|
|
467
|
-
SuffixEnum.official;
|
|
468
|
-
// 生成文件名
|
|
469
|
-
const extensionName = `.${file.name.split('.').pop()}`;
|
|
470
|
-
const { year, fileName } = this.generateFileName(imageType, extensionName);
|
|
471
378
|
// 注释:小图片(≤100KB)不压缩,quality设为1 (MAIN-2481)
|
|
472
379
|
const isSmallImage = getFileSizeInKB(file.size) <= SMALL_IMAGE_THRESHOLD;
|
|
473
380
|
let quality = isSmallImage ? 1 : option.quality || DEFAULT_QUALITY;
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const val = {
|
|
484
|
-
...result,
|
|
485
|
-
imgUrl: `${result.bucket}://${result.name}`,
|
|
486
|
-
imgOrgUrl: `${result.bucket}://${result.name}`,
|
|
487
|
-
fileName: file.name,
|
|
488
|
-
};
|
|
489
|
-
callbacks.onSuccess(val);
|
|
490
|
-
resolve(val);
|
|
491
|
-
}
|
|
492
|
-
else {
|
|
493
|
-
callbacks.onError('上传失败');
|
|
494
|
-
reject('上传失败');
|
|
495
|
-
}
|
|
496
|
-
return;
|
|
381
|
+
const businessType = option.businessType;
|
|
382
|
+
if (businessType === BusinessType.CREDIT) {
|
|
383
|
+
return await this.upload({
|
|
384
|
+
file,
|
|
385
|
+
businessType,
|
|
386
|
+
resolve,
|
|
387
|
+
reject,
|
|
388
|
+
callbacks,
|
|
389
|
+
});
|
|
497
390
|
}
|
|
498
391
|
// 注释:batchTransfer选项 - 批量转换水印(用于某些特殊业务场景)
|
|
499
392
|
if (option.batchTransfer && this.apiConfig.multiTransferImage) {
|
|
@@ -502,7 +395,6 @@ export class AliOssClass {
|
|
|
502
395
|
this.loadImage({
|
|
503
396
|
url: res.data.data,
|
|
504
397
|
val: res.data,
|
|
505
|
-
suffix,
|
|
506
398
|
option,
|
|
507
399
|
file,
|
|
508
400
|
resolve,
|
|
@@ -523,7 +415,6 @@ export class AliOssClass {
|
|
|
523
415
|
this.loadImage({
|
|
524
416
|
url: res.data.data,
|
|
525
417
|
val: res.data,
|
|
526
|
-
suffix,
|
|
527
418
|
option,
|
|
528
419
|
file,
|
|
529
420
|
resolve,
|
|
@@ -536,15 +427,13 @@ export class AliOssClass {
|
|
|
536
427
|
}
|
|
537
428
|
return;
|
|
538
429
|
}
|
|
539
|
-
// 预加载图片获取尺寸
|
|
540
|
-
const image = await this.getImageInfo(file);
|
|
541
430
|
// 图片压缩
|
|
542
431
|
const rst = await lrz(file, { quality });
|
|
543
432
|
// 注释:长图压缩有问题,长宽比>2:1的图片不压缩
|
|
544
|
-
const isLong = this.isLongImage(
|
|
433
|
+
const isLong = (await this.isLongImage(file)) || false;
|
|
545
434
|
// 确定最终上传的文件
|
|
546
435
|
let postFile;
|
|
547
|
-
if (option.notCompress ||
|
|
436
|
+
if (option.notCompress || file.type === 'image/gif' || isLong) {
|
|
548
437
|
// 注释:GIF、notCompress选项或长图不压缩
|
|
549
438
|
postFile = file;
|
|
550
439
|
}
|
|
@@ -567,23 +456,22 @@ export class AliOssClass {
|
|
|
567
456
|
writable: false,
|
|
568
457
|
});
|
|
569
458
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
callbacks
|
|
576
|
-
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
459
|
+
const result = await this.upload({
|
|
460
|
+
file,
|
|
461
|
+
businessType,
|
|
462
|
+
resolve,
|
|
463
|
+
reject,
|
|
464
|
+
callbacks,
|
|
465
|
+
});
|
|
579
466
|
// 注释:idCard场景 - 生成预签名URL
|
|
580
|
-
if (idCard
|
|
467
|
+
if ((option.idCard || false) &&
|
|
468
|
+
this.apiConfig.generatePrePresignedUrl) {
|
|
581
469
|
const res = await this.apiConfig.generatePrePresignedUrl({
|
|
582
470
|
objectId: result.name,
|
|
583
471
|
expireMils: PRESIGNED_URL_EXPIRE_TIME,
|
|
584
472
|
});
|
|
585
473
|
if (res.data.code === 0) {
|
|
586
|
-
this.
|
|
474
|
+
this.loadImage({
|
|
587
475
|
url: res.data.data,
|
|
588
476
|
val: result,
|
|
589
477
|
file: postFile,
|
|
@@ -598,41 +486,6 @@ export class AliOssClass {
|
|
|
598
486
|
}
|
|
599
487
|
return;
|
|
600
488
|
}
|
|
601
|
-
// 注释:avatar场景 - 调用aliyunPersist处理
|
|
602
|
-
if (imageType === 'avatar' && this.apiConfig.aliyunPersist) {
|
|
603
|
-
const res = await this.apiConfig.aliyunPersist({
|
|
604
|
-
object: `${result.bucket}://${result.name}`,
|
|
605
|
-
businessType: BusinessType.AVATAR,
|
|
606
|
-
});
|
|
607
|
-
if (res.data.code === 0) {
|
|
608
|
-
this.loadImageNew({
|
|
609
|
-
url: res.data.data,
|
|
610
|
-
val: result,
|
|
611
|
-
file: postFile,
|
|
612
|
-
option,
|
|
613
|
-
resolve,
|
|
614
|
-
reject,
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
else {
|
|
618
|
-
callbacks.onError('头像处理失败');
|
|
619
|
-
reject('头像处理失败');
|
|
620
|
-
}
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
// 注释:标准场景 - 使用client.options中的realmName和suffix
|
|
624
|
-
const clientParams = client.options;
|
|
625
|
-
const url = `${clientParams.realmName}${result.name}${clientParams.suffix || ''}`;
|
|
626
|
-
this.loadImage({
|
|
627
|
-
url,
|
|
628
|
-
val: result,
|
|
629
|
-
suffix,
|
|
630
|
-
file: postFile,
|
|
631
|
-
option,
|
|
632
|
-
resolve,
|
|
633
|
-
reject,
|
|
634
|
-
isGif,
|
|
635
|
-
});
|
|
636
489
|
}
|
|
637
490
|
catch (error) {
|
|
638
491
|
const errorMsg = error.message || '上传异常';
|
|
@@ -651,6 +504,9 @@ export class AliOssClass {
|
|
|
651
504
|
if (!(option.file instanceof File)) {
|
|
652
505
|
return Promise.reject('file is not instanceof File');
|
|
653
506
|
}
|
|
507
|
+
if (!option.businessType) {
|
|
508
|
+
return Promise.reject('businessType不能为空');
|
|
509
|
+
}
|
|
654
510
|
const file = option.file;
|
|
655
511
|
// 初始化回调函数
|
|
656
512
|
const callbacks = {
|
|
@@ -663,15 +519,9 @@ export class AliOssClass {
|
|
|
663
519
|
option.onProgress = callbacks.onProgress;
|
|
664
520
|
return new Promise(async (resolve, reject) => {
|
|
665
521
|
try {
|
|
666
|
-
// 生成文件名
|
|
667
|
-
const extensionName = `.${file.name.split('.').pop()}`;
|
|
668
|
-
const fileType = extensionName === '.apk' ? 'apk' : 'file';
|
|
669
|
-
const { year, fileName } = this.generateFileName(fileType, extensionName);
|
|
670
522
|
const isVideo = !!option.isVideo;
|
|
671
523
|
const isDocument = !!option.isDocument;
|
|
672
524
|
const isUnProtect = !!option.isUnProtect;
|
|
673
|
-
// 注释:根据不同场景选择businessType
|
|
674
|
-
// isUnProtect -> 8, isVideo -> 6, isDocument -> 6, 其他 -> 1500
|
|
675
525
|
let businessType = option.businessType;
|
|
676
526
|
if (!businessType) {
|
|
677
527
|
if (isUnProtect) {
|
|
@@ -684,27 +534,13 @@ export class AliOssClass {
|
|
|
684
534
|
businessType = BusinessType.OTHER;
|
|
685
535
|
}
|
|
686
536
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
const clientParams = client.options;
|
|
695
|
-
const url = `${clientParams.realmName}${result.name}${clientParams.suffix || ''}`;
|
|
696
|
-
const uploadResult = {
|
|
697
|
-
...result,
|
|
698
|
-
url,
|
|
699
|
-
fileName: file.name,
|
|
700
|
-
};
|
|
701
|
-
callbacks.onSuccess(uploadResult);
|
|
702
|
-
resolve(uploadResult);
|
|
703
|
-
}
|
|
704
|
-
else {
|
|
705
|
-
callbacks.onError('上传失败');
|
|
706
|
-
reject('上传失败');
|
|
707
|
-
}
|
|
537
|
+
await this.upload({
|
|
538
|
+
file,
|
|
539
|
+
businessType,
|
|
540
|
+
resolve,
|
|
541
|
+
reject,
|
|
542
|
+
callbacks,
|
|
543
|
+
});
|
|
708
544
|
}
|
|
709
545
|
catch (error) {
|
|
710
546
|
const errorMsg = error.message || '上传异常';
|
|
@@ -714,8 +550,17 @@ export class AliOssClass {
|
|
|
714
550
|
});
|
|
715
551
|
};
|
|
716
552
|
/**
|
|
717
|
-
*
|
|
718
|
-
*
|
|
553
|
+
* 纯图片上传 pureOssUploadImage
|
|
554
|
+
* 注释:图片上传,不压缩
|
|
555
|
+
* @param option 上传选项
|
|
556
|
+
* @returns Promise
|
|
557
|
+
*/
|
|
558
|
+
pureOssUploadImage = async (option) => {
|
|
559
|
+
return await this.shopDetailUpdate(option);
|
|
560
|
+
};
|
|
561
|
+
/**
|
|
562
|
+
* 商品详情图片上传(不建议直接使用,请使用 pureOssUploadImage)
|
|
563
|
+
* 注释:图片上传,不压缩
|
|
719
564
|
* @param option 上传选项
|
|
720
565
|
* @returns Promise
|
|
721
566
|
*/
|
|
@@ -723,6 +568,9 @@ export class AliOssClass {
|
|
|
723
568
|
if (!(option.file instanceof File)) {
|
|
724
569
|
return Promise.reject('file is not instanceof File');
|
|
725
570
|
}
|
|
571
|
+
if (!option.businessType) {
|
|
572
|
+
return Promise.reject('businessType不能为空');
|
|
573
|
+
}
|
|
726
574
|
const file = option.file;
|
|
727
575
|
// 初始化回调函数
|
|
728
576
|
const callbacks = {
|
|
@@ -741,6 +589,7 @@ export class AliOssClass {
|
|
|
741
589
|
}
|
|
742
590
|
return new Promise(async (resolve, reject) => {
|
|
743
591
|
try {
|
|
592
|
+
const businessType = option.businessType;
|
|
744
593
|
// 文件大小验证
|
|
745
594
|
if (getFileSizeInMB(file.size) > DEFAULT_FILE_SIZE_LIMIT) {
|
|
746
595
|
const errorMsg = `图片不能超过${DEFAULT_FILE_SIZE_LIMIT}M`;
|
|
@@ -748,34 +597,13 @@ export class AliOssClass {
|
|
|
748
597
|
reject(errorMsg);
|
|
749
598
|
return;
|
|
750
599
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
const { year, fileName } = this.generateFileName(imageType, extensionName);
|
|
759
|
-
// 使用NoWater客户端
|
|
760
|
-
const client = await this.createOssClient(BusinessType.OTHER);
|
|
761
|
-
const result = await this.performOssUpload(client, fileName, file, year, file.type, callbacks.onProgress);
|
|
762
|
-
if (result.res.statusCode === 200) {
|
|
763
|
-
const clientParams = client.options;
|
|
764
|
-
const url = `${clientParams.realmName}${result.name}${clientParams.suffix || ''}`;
|
|
765
|
-
this.loadImage({
|
|
766
|
-
url,
|
|
767
|
-
val: result,
|
|
768
|
-
suffix,
|
|
769
|
-
file,
|
|
770
|
-
option,
|
|
771
|
-
resolve,
|
|
772
|
-
reject,
|
|
773
|
-
});
|
|
774
|
-
}
|
|
775
|
-
else {
|
|
776
|
-
callbacks.onError('上传失败');
|
|
777
|
-
reject('上传失败');
|
|
778
|
-
}
|
|
600
|
+
await this.upload({
|
|
601
|
+
file,
|
|
602
|
+
businessType,
|
|
603
|
+
resolve,
|
|
604
|
+
reject,
|
|
605
|
+
callbacks,
|
|
606
|
+
});
|
|
779
607
|
}
|
|
780
608
|
catch (error) {
|
|
781
609
|
const errorMsg = error.message || '上传异常';
|
|
@@ -784,15 +612,6 @@ export class AliOssClass {
|
|
|
784
612
|
}
|
|
785
613
|
});
|
|
786
614
|
};
|
|
787
|
-
/**
|
|
788
|
-
* 生成随机字符串
|
|
789
|
-
* @param num 字符串长度
|
|
790
|
-
*/
|
|
791
|
-
randomString = randomString;
|
|
792
|
-
/**
|
|
793
|
-
* 后缀枚举
|
|
794
|
-
*/
|
|
795
|
-
suffixEnum = SuffixEnum;
|
|
796
615
|
/**
|
|
797
616
|
* 业务类型枚举
|
|
798
617
|
*/
|
|
@@ -812,5 +631,4 @@ export function createAliOssUploader(apiConfig) {
|
|
|
812
631
|
export default {
|
|
813
632
|
createAliOssUploader,
|
|
814
633
|
BusinessType,
|
|
815
|
-
SuffixEnum,
|
|
816
634
|
};
|