@haluo/util 2.0.23 → 2.0.25

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.
@@ -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/jpg',
61
- 'image/jpeg',
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
- // NO_WATER = 0, // 不打水印
89
- // AVATAR = 1, // 头像
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";
100
- BusinessType[BusinessType["USED_CAR"] = 171978237] = "USED_CAR";
94
+ /** 二手车 */
95
+ BusinessType[BusinessType["USEDCAR"] = 171978237] = "USEDCAR";
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";
110
- BusinessType[BusinessType["DRIVING_SCHOOL"] = 172137757] = "DRIVING_SCHOOL";
114
+ /** 驾校 */
115
+ BusinessType[BusinessType["DRIVINGSCHOOL"] = 172137757] = "DRIVINGSCHOOL";
116
+ /** 评论图片 */
111
117
  BusinessType[BusinessType["REPLY"] = 195929698] = "REPLY";
118
+ /** 厂家 */
112
119
  BusinessType[BusinessType["FACTORY"] = 118733601] = "FACTORY";
113
- BusinessType[BusinessType["CYCLING_DATA"] = 148681294] = "CYCLING_DATA";
120
+ /** 骑行数据 */
121
+ BusinessType[BusinessType["CYCLINGDATA"] = 148681294] = "CYCLINGDATA";
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,60 @@ 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
- this.clientCache = new Map();
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(file, businessType, size) {
218
+ async upload(params) {
219
+ const { file, businessType, resolve, reject, callbacks } = params;
220
+ let { size } = params;
221
+ let result = {};
211
222
  try {
212
- // -------------------------- 步骤 1:请求后端获取所有签名字段 --------------------------
223
+ if (!size && this.isImageFile(file)) {
224
+ const image = await this.getImageInfo(file);
225
+ size = `_${image?.width}_${image?.height}`;
226
+ }
227
+ // 步骤 1:请求后端获取所有签名字段
213
228
  const signResponse = await this.apiConfig.getSts({
214
- businessType,
229
+ businessType: businessType,
215
230
  docType: file.type,
216
- size, // _1024_567(可选)
231
+ size: size, // _1024_567(可选)
217
232
  });
218
233
  if (signResponse.data.code !== 0) {
219
- console.error(signResponse);
220
- throw new Error(`获取签名失败:${signResponse.data.message}`);
234
+ const errorMessage = `获取签名失败:${signResponse.data.message}`;
235
+ callbacks?.onError(errorMessage);
236
+ reject && reject(errorMessage);
237
+ console.error(errorMessage);
221
238
  }
222
239
  const signData = await signResponse.data.data;
223
240
  console.log('后端返回的签名字段:', signData);
224
- // -------------------------- 步骤 2:构造 FormData(字段名必须和后端返回一致)--------------------------
241
+ // 步骤 2:构造 FormData(字段名必须和后端返回一致
225
242
  const formData = new FormData();
226
- formData.append('autherid', signData['autherid']); // 业务字段(原样传)
227
- formData.append('uid', signData['uid']); // 业务字段(原样传)
243
+ // formData.append('autherid', signData['autherid'])
244
+ // formData.append('uid', signData['uid'])
228
245
  formData.append('key', signData['key']);
229
246
  formData.append('policy', signData['policy']);
230
247
  formData.append('success_action_status', '200');
@@ -236,77 +253,38 @@ export class AliOssClass {
236
253
  formData.append('x-oss-signature', signData['signature']);
237
254
  formData.append('x-oss-signature-version', signData['signatureVersion']);
238
255
  formData.append('file', file);
239
- // -------------------------- 步骤 3:发送 POST 请求到 OSS --------------------------
256
+ // 步骤 3:发送 POST 请求到 OSS
240
257
  const uploadResponse = await fetch(signData.uploadUrl, {
241
258
  method: 'POST',
242
259
  body: formData,
243
260
  // 重点:不要手动设置 Content-Type!浏览器会自动处理为 multipart/form-data 并带边界符
244
261
  headers: { Accept: '*/*' },
245
262
  });
246
- // -------------------------- 步骤 4:处理上传结果 --------------------------
263
+ // 步骤 4:处理上传结果
247
264
  const responseText = await uploadResponse.text();
248
- console.log('responseText', responseText);
249
265
  if (uploadResponse.ok) {
250
- console.log('上传成功,文件路径:', signData['key'], signData['fileUrl']);
266
+ result = {
267
+ imgUrl: signData['fileUrl'],
268
+ imgOrgUrl: signData['fileUrl'],
269
+ name: file.name,
270
+ fileName: file.name,
271
+ };
272
+ callbacks?.onSuccess(result);
273
+ resolve && resolve(result);
251
274
  }
252
275
  else {
253
- throw new Error(`上传失败(OSS 返回 ${uploadResponse.status}):${responseText}`);
276
+ const errorMessage = `上传失败(OSS 返回 ${uploadResponse.status}):${responseText}`;
277
+ callbacks?.onError(errorMessage);
278
+ reject && reject(errorMessage);
279
+ console.error(errorMessage);
254
280
  }
255
281
  }
256
282
  catch (err) {
257
- console.error('详细错误:', err);
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配置失败');
283
+ callbacks?.onError(err.message);
284
+ reject && reject(err.message);
285
+ console.error('上传异常:', err.message);
274
286
  }
275
- const clientParams = res.data.data || {};
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
- });
287
+ return result;
310
288
  }
311
289
  /**
312
290
  * 预加载图片获取尺寸
@@ -329,99 +307,33 @@ export class AliOssClass {
329
307
  image.src = objectUrl;
330
308
  });
331
309
  }
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
310
  /**
348
311
  * 检查图片是否为长图
349
- * @param width 图片宽度
350
- * @param height 图片高度
312
+ * @param File 图片
351
313
  * @returns 是否为长图
352
314
  */
353
- isLongImage(width, height) {
315
+ async isLongImage(file) {
316
+ // 预加载图片获取尺寸
317
+ const image = await this.getImageInfo(file);
318
+ const { width, height } = image;
354
319
  const maxDimension = Math.max(width, height);
355
320
  const minDimension = Math.min(width, height);
356
321
  return maxDimension / minDimension > LONG_IMAGE_RATIO;
357
322
  }
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
323
  /**
384
324
  * 加载图片并返回结果
385
325
  * 用于标准的图片上传场景
386
326
  * @param params 加载参数
387
327
  */
388
328
  loadImage(params) {
389
- const { url, val, suffix, file, option, resolve, reject, isGif } = params;
329
+ const { url, val, file, option, resolve, reject } = params;
390
330
  let img = new Image();
391
331
  img.src = url;
392
332
  img.onload = function () {
393
333
  if (!img)
394
334
  return;
395
335
  val.imgOrgUrl = `${url}?_${img.width}_${img.height}`;
396
- // 注释:全景图和GIF图片缩略图后缀不带300,其他需要
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;
336
+ val.imgUrl = val.imgOrgUrl;
425
337
  val.name = file.name;
426
338
  val.fileName = file.name;
427
339
  option.onSuccess?.(val);
@@ -444,6 +356,9 @@ export class AliOssClass {
444
356
  if (!(option.file instanceof File)) {
445
357
  return Promise.reject('file is not instanceof File');
446
358
  }
359
+ if (!option.businessType) {
360
+ return Promise.reject('businessType不能为空');
361
+ }
447
362
  const file = option.file;
448
363
  // 初始化回调函数
449
364
  const callbacks = {
@@ -462,38 +377,18 @@ export class AliOssClass {
462
377
  }
463
378
  return new Promise(async (resolve, reject) => {
464
379
  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
380
  // 注释:小图片(≤100KB)不压缩,quality设为1 (MAIN-2481)
472
381
  const isSmallImage = getFileSizeInKB(file.size) <= SMALL_IMAGE_THRESHOLD;
473
382
  let quality = isSmallImage ? 1 : option.quality || DEFAULT_QUALITY;
474
- const isGif = file.type === 'image/gif';
475
- const idCard = option.idCard || false;
476
- // 确定业务类型
477
- const businessType = this.determineBusinessType(option, isGif);
478
- // 注释:特殊业务类型1299(加密)直接返回bucket路径,不加载图片
479
- if (businessType === BusinessType.OTHER) {
480
- const client = await this.createOssClient(businessType);
481
- const result = await this.performOssUpload(client, fileName, file, year, file.type, callbacks.onProgress);
482
- if (result.res.statusCode === 200) {
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;
383
+ const businessType = option.businessType;
384
+ if (businessType === BusinessType.CREDIT) {
385
+ return await this.upload({
386
+ file,
387
+ businessType,
388
+ resolve,
389
+ reject,
390
+ callbacks,
391
+ });
497
392
  }
498
393
  // 注释:batchTransfer选项 - 批量转换水印(用于某些特殊业务场景)
499
394
  if (option.batchTransfer && this.apiConfig.multiTransferImage) {
@@ -502,7 +397,6 @@ export class AliOssClass {
502
397
  this.loadImage({
503
398
  url: res.data.data,
504
399
  val: res.data,
505
- suffix,
506
400
  option,
507
401
  file,
508
402
  resolve,
@@ -523,7 +417,6 @@ export class AliOssClass {
523
417
  this.loadImage({
524
418
  url: res.data.data,
525
419
  val: res.data,
526
- suffix,
527
420
  option,
528
421
  file,
529
422
  resolve,
@@ -536,15 +429,13 @@ export class AliOssClass {
536
429
  }
537
430
  return;
538
431
  }
539
- // 预加载图片获取尺寸
540
- const image = await this.getImageInfo(file);
541
432
  // 图片压缩
542
433
  const rst = await lrz(file, { quality });
543
434
  // 注释:长图压缩有问题,长宽比>2:1的图片不压缩
544
- const isLong = this.isLongImage(image.width, image.height);
435
+ const isLong = (await this.isLongImage(file)) || false;
545
436
  // 确定最终上传的文件
546
437
  let postFile;
547
- if (option.notCompress || isGif || isLong) {
438
+ if (option.notCompress || file.type === 'image/gif' || isLong) {
548
439
  // 注释:GIF、notCompress选项或长图不压缩
549
440
  postFile = file;
550
441
  }
@@ -567,23 +458,22 @@ export class AliOssClass {
567
458
  writable: false,
568
459
  });
569
460
  }
570
- // 注释:根据不同场景选择不同的OSS客户端
571
- // GIF -> UnProtect(8), idCard -> Private(1199), nowater/forum -> NoWater(0), 其他 -> 默认
572
- const client = await this.createOssClient(businessType);
573
- const result = await this.performOssUpload(client, fileName, postFile, year, postFile.type, callbacks.onProgress);
574
- if (result.res.statusCode !== 200) {
575
- callbacks.onError('上传失败');
576
- reject('上传失败');
577
- return;
578
- }
461
+ const result = await this.upload({
462
+ file,
463
+ businessType,
464
+ resolve,
465
+ reject,
466
+ callbacks,
467
+ });
579
468
  // 注释:idCard场景 - 生成预签名URL
580
- if (idCard && this.apiConfig.generatePrePresignedUrl) {
469
+ if ((option.idCard || false) &&
470
+ this.apiConfig.generatePrePresignedUrl) {
581
471
  const res = await this.apiConfig.generatePrePresignedUrl({
582
472
  objectId: result.name,
583
473
  expireMils: PRESIGNED_URL_EXPIRE_TIME,
584
474
  });
585
475
  if (res.data.code === 0) {
586
- this.loadImageNew({
476
+ this.loadImage({
587
477
  url: res.data.data,
588
478
  val: result,
589
479
  file: postFile,
@@ -598,41 +488,6 @@ export class AliOssClass {
598
488
  }
599
489
  return;
600
490
  }
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
491
  }
637
492
  catch (error) {
638
493
  const errorMsg = error.message || '上传异常';
@@ -651,6 +506,9 @@ export class AliOssClass {
651
506
  if (!(option.file instanceof File)) {
652
507
  return Promise.reject('file is not instanceof File');
653
508
  }
509
+ if (!option.businessType) {
510
+ return Promise.reject('businessType不能为空');
511
+ }
654
512
  const file = option.file;
655
513
  // 初始化回调函数
656
514
  const callbacks = {
@@ -663,15 +521,9 @@ export class AliOssClass {
663
521
  option.onProgress = callbacks.onProgress;
664
522
  return new Promise(async (resolve, reject) => {
665
523
  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
524
  const isVideo = !!option.isVideo;
671
525
  const isDocument = !!option.isDocument;
672
526
  const isUnProtect = !!option.isUnProtect;
673
- // 注释:根据不同场景选择businessType
674
- // isUnProtect -> 8, isVideo -> 6, isDocument -> 6, 其他 -> 1500
675
527
  let businessType = option.businessType;
676
528
  if (!businessType) {
677
529
  if (isUnProtect) {
@@ -684,27 +536,13 @@ export class AliOssClass {
684
536
  businessType = BusinessType.OTHER;
685
537
  }
686
538
  }
687
- // 注释:文档上传可能需要先调用uploadConf获取配置
688
- if (isDocument && this.apiConfig.uploadConf) {
689
- await this.apiConfig.uploadConf(businessType);
690
- }
691
- const client = await this.createOssClient(businessType);
692
- const result = await this.performOssUpload(client, fileName, file, year, file.type, callbacks.onProgress);
693
- if (result.res.statusCode === 200) {
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
- }
539
+ await this.upload({
540
+ file,
541
+ businessType,
542
+ resolve,
543
+ reject,
544
+ callbacks,
545
+ });
708
546
  }
709
547
  catch (error) {
710
548
  const errorMsg = error.message || '上传异常';
@@ -714,8 +552,17 @@ export class AliOssClass {
714
552
  });
715
553
  };
716
554
  /**
717
- * 商品详情图片上传
718
- * 注释:此方法用于商品详情页的图片上传,不压缩,使用NoWater客户端
555
+ * 纯图片上传 pureOssUploadImage
556
+ * 注释:图片上传,不压缩
557
+ * @param option 上传选项
558
+ * @returns Promise
559
+ */
560
+ pureOssUploadImage = async (option) => {
561
+ return await this.shopDetailUpdate(option);
562
+ };
563
+ /**
564
+ * 商品详情图片上传(不建议直接使用,请使用 pureOssUploadImage)
565
+ * 注释:图片上传,不压缩
719
566
  * @param option 上传选项
720
567
  * @returns Promise
721
568
  */
@@ -723,6 +570,9 @@ export class AliOssClass {
723
570
  if (!(option.file instanceof File)) {
724
571
  return Promise.reject('file is not instanceof File');
725
572
  }
573
+ if (!option.businessType) {
574
+ return Promise.reject('businessType不能为空');
575
+ }
726
576
  const file = option.file;
727
577
  // 初始化回调函数
728
578
  const callbacks = {
@@ -741,6 +591,7 @@ export class AliOssClass {
741
591
  }
742
592
  return new Promise(async (resolve, reject) => {
743
593
  try {
594
+ const businessType = option.businessType;
744
595
  // 文件大小验证
745
596
  if (getFileSizeInMB(file.size) > DEFAULT_FILE_SIZE_LIMIT) {
746
597
  const errorMsg = `图片不能超过${DEFAULT_FILE_SIZE_LIMIT}M`;
@@ -748,34 +599,13 @@ export class AliOssClass {
748
599
  reject(errorMsg);
749
600
  return;
750
601
  }
751
- // 预加载图片(虽然不需要尺寸,但保持原有逻辑)
752
- await this.getImageInfo(file);
753
- const imageType = option.imageType || DEFAULT_IMAGE_TYPE;
754
- const suffix = SuffixEnum[imageType] ||
755
- SuffixEnum.official;
756
- // 生成文件名
757
- const extensionName = `.${file.name.split('.').pop()}`;
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
- }
602
+ await this.upload({
603
+ file,
604
+ businessType,
605
+ resolve,
606
+ reject,
607
+ callbacks,
608
+ });
779
609
  }
780
610
  catch (error) {
781
611
  const errorMsg = error.message || '上传异常';
@@ -784,15 +614,6 @@ export class AliOssClass {
784
614
  }
785
615
  });
786
616
  };
787
- /**
788
- * 生成随机字符串
789
- * @param num 字符串长度
790
- */
791
- randomString = randomString;
792
- /**
793
- * 后缀枚举
794
- */
795
- suffixEnum = SuffixEnum;
796
617
  /**
797
618
  * 业务类型枚举
798
619
  */
@@ -812,5 +633,4 @@ export function createAliOssUploader(apiConfig) {
812
633
  export default {
813
634
  createAliOssUploader,
814
635
  BusinessType,
815
- SuffixEnum,
816
636
  };