@gateweb/react-utils 1.11.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.d.ts +63 -4
- package/dist/cjs/index.js +100 -23
- package/dist/es/index.d.mts +63 -4
- package/dist/es/index.mjs +97 -24
- package/package.json +1 -1
package/dist/cjs/index.d.ts
CHANGED
|
@@ -474,11 +474,33 @@ declare const fakeApi: <T>(returnValue: T, result?: boolean, time?: number) => P
|
|
|
474
474
|
message: string;
|
|
475
475
|
}>;
|
|
476
476
|
|
|
477
|
+
declare const MimeTypeMap: {
|
|
478
|
+
readonly jpeg: "image/jpeg";
|
|
479
|
+
readonly jpg: "image/jpeg";
|
|
480
|
+
readonly gif: "image/gif";
|
|
481
|
+
readonly png: "image/png";
|
|
482
|
+
readonly pdf: "application/pdf";
|
|
483
|
+
readonly zip: "application/zip";
|
|
484
|
+
readonly csv: "text/csv";
|
|
485
|
+
readonly ppt: "application/vnd.ms-powerpoint";
|
|
486
|
+
readonly pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
|
487
|
+
readonly xls: "application/vnd.ms-excel";
|
|
488
|
+
readonly xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
489
|
+
readonly doc: "application/msword";
|
|
490
|
+
readonly docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
491
|
+
readonly txt: "text/plain";
|
|
492
|
+
};
|
|
493
|
+
declare const OtherMimeType = "application/octet-stream";
|
|
494
|
+
type MimeTypeExtension = keyof typeof MimeTypeMap;
|
|
495
|
+
type MimeTypeValue = (typeof MimeTypeMap)[MimeTypeExtension] | typeof OtherMimeType;
|
|
496
|
+
|
|
477
497
|
/**
|
|
478
|
-
*
|
|
498
|
+
* 檢查檔案是否為合法的檔案類型
|
|
499
|
+
*
|
|
500
|
+
* `accepts` 可同時接受副檔名以及 MIME 類型
|
|
479
501
|
*
|
|
480
502
|
* @param file 檔案
|
|
481
|
-
* @param accepts
|
|
503
|
+
* @param accepts 允許的類型
|
|
482
504
|
*
|
|
483
505
|
* @example
|
|
484
506
|
*
|
|
@@ -486,6 +508,7 @@ declare const fakeApi: <T>(returnValue: T, result?: boolean, time?: number) => P
|
|
|
486
508
|
* validateFileType({ type: 'image/png' }, ['image/png', 'image/jpeg']) // true
|
|
487
509
|
* validateFileType({ type: 'image/png' }, ['image/jpeg']) // false
|
|
488
510
|
* validateFileType({ type: 'image/png' }, ['image/*']) // true
|
|
511
|
+
* validateFileType({ name: '圖片.png', type: 'image/png' }, ['.png']) // true
|
|
489
512
|
* ```
|
|
490
513
|
*/
|
|
491
514
|
declare const validateFileType: (file: File, accepts: string[]) => boolean;
|
|
@@ -507,8 +530,44 @@ declare const validateFileType: (file: File, accepts: string[]) => boolean;
|
|
|
507
530
|
* getMimeType('txt') // 'text/plain'
|
|
508
531
|
* getMimeType('zip') // 'application/zip'
|
|
509
532
|
* getMimeType('mp4') // 'application/octet-stream'
|
|
533
|
+
* getMimeType('xls') // 'application/vnd.ms-excel'
|
|
534
|
+
* getMimeType('xlsx') // 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
535
|
+
* getMimeType('doc') // 'application/msword'
|
|
536
|
+
* getMimeType('docx') // 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
537
|
+
*
|
|
538
|
+
* @deprecated use `parseFileInfoFromFilename` instead
|
|
539
|
+
*/
|
|
540
|
+
declare const getMimeType: (fileName: string) => "image/jpeg" | "image/gif" | "image/png" | "application/pdf" | "application/zip" | "text/csv" | "application/vnd.ms-powerpoint" | "application/vnd.openxmlformats-officedocument.presentationml.presentation" | "application/vnd.ms-excel" | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | "application/msword" | "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "text/plain";
|
|
541
|
+
/**
|
|
542
|
+
* 用來解析後端在 response header content-disposition 的內容
|
|
543
|
+
*
|
|
544
|
+
* 一般來說格式會有以下兩種
|
|
545
|
+
*
|
|
546
|
+
* - Content-Disposition: attachment; filename="file name.jpg"
|
|
547
|
+
* - Content-Disposition: attachment; filename*=UTF-8''file%20name2.jpg
|
|
548
|
+
*
|
|
549
|
+
* 如果格式正確就會取得檔案名稱(包含副檔名),優先取 filename* 內的內容
|
|
550
|
+
*
|
|
551
|
+
* @param disposition Content-Disposition
|
|
552
|
+
*
|
|
553
|
+
* @example
|
|
554
|
+
*
|
|
555
|
+
* parseFilenameFromDisposition('attachment; filename="file name1.jpg') // file name.jpg
|
|
556
|
+
* parseFilenameFromDisposition('attachment; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
|
|
557
|
+
* parseFilenameFromDisposition('attachment; filename="file name.jpg; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
|
|
558
|
+
*/
|
|
559
|
+
declare const parseFilenameFromDisposition: (disposition: string) => string | undefined;
|
|
560
|
+
/**
|
|
561
|
+
* 解析 `filename` 回傳檔名、副檔名、MIME type
|
|
562
|
+
*
|
|
563
|
+
* @param filename 檔案名稱
|
|
564
|
+
*
|
|
565
|
+
* @example
|
|
566
|
+
*
|
|
567
|
+
* parseFileInfoFromFilename('image.jpg') // ['image', 'jpg', 'image/jpeg']
|
|
568
|
+
* parseFileInfoFromFilename('image') // ['image', '', 'application/octet-stream']
|
|
510
569
|
*/
|
|
511
|
-
declare const
|
|
570
|
+
declare const parseFileInfoFromFilename: (filename: string) => [string, string, MimeTypeValue];
|
|
512
571
|
|
|
513
572
|
declare function invariant(condition: any, message?: string | (() => string)): asserts condition;
|
|
514
573
|
|
|
@@ -1145,4 +1204,4 @@ declare const getLocalStorage: <T>(key: string, deCode?: boolean) => T | undefin
|
|
|
1145
1204
|
*/
|
|
1146
1205
|
declare const setLocalStorage: (key: string, value: Record<string, any>, enCode?: boolean) => void;
|
|
1147
1206
|
|
|
1148
|
-
export { ByteSize, type PartialBy, QueryProvider, type RequiredBy, type TCountdownActions, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createDataContext, createEnumLikeObject, debounce, deepMerge, downloadFile, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getLocalStorage, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeRefs, objectToSearchParams, omit, omitByValue, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, setLocalStorage, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useCountdown, useQueryContext, useValue, validTaxId, validateDateString, validateFileType, wait };
|
|
1207
|
+
export { ByteSize, type MimeTypeExtension, MimeTypeMap, type MimeTypeValue, OtherMimeType, type PartialBy, QueryProvider, type RequiredBy, type TCountdownActions, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createDataContext, createEnumLikeObject, debounce, deepMerge, downloadFile, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getLocalStorage, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeRefs, objectToSearchParams, omit, omitByValue, parseFileInfoFromFilename, parseFilenameFromDisposition, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, setLocalStorage, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useCountdown, useQueryContext, useValue, validTaxId, validateDateString, validateFileType, wait };
|
package/dist/cjs/index.js
CHANGED
|
@@ -238,11 +238,31 @@ function createEnumLikeObject(obj, name, scene) {
|
|
|
238
238
|
}, time);
|
|
239
239
|
});
|
|
240
240
|
|
|
241
|
+
const MimeTypeMap = {
|
|
242
|
+
jpeg: 'image/jpeg',
|
|
243
|
+
jpg: 'image/jpeg',
|
|
244
|
+
gif: 'image/gif',
|
|
245
|
+
png: 'image/png',
|
|
246
|
+
pdf: 'application/pdf',
|
|
247
|
+
zip: 'application/zip',
|
|
248
|
+
csv: 'text/csv',
|
|
249
|
+
ppt: 'application/vnd.ms-powerpoint',
|
|
250
|
+
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
251
|
+
xls: 'application/vnd.ms-excel',
|
|
252
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
253
|
+
doc: 'application/msword',
|
|
254
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
255
|
+
txt: 'text/plain'
|
|
256
|
+
};
|
|
257
|
+
const OtherMimeType = 'application/octet-stream';
|
|
258
|
+
|
|
241
259
|
/**
|
|
242
|
-
*
|
|
260
|
+
* 檢查檔案是否為合法的檔案類型
|
|
261
|
+
*
|
|
262
|
+
* `accepts` 可同時接受副檔名以及 MIME 類型
|
|
243
263
|
*
|
|
244
264
|
* @param file 檔案
|
|
245
|
-
* @param accepts
|
|
265
|
+
* @param accepts 允許的類型
|
|
246
266
|
*
|
|
247
267
|
* @example
|
|
248
268
|
*
|
|
@@ -250,21 +270,24 @@ function createEnumLikeObject(obj, name, scene) {
|
|
|
250
270
|
* validateFileType({ type: 'image/png' }, ['image/png', 'image/jpeg']) // true
|
|
251
271
|
* validateFileType({ type: 'image/png' }, ['image/jpeg']) // false
|
|
252
272
|
* validateFileType({ type: 'image/png' }, ['image/*']) // true
|
|
273
|
+
* validateFileType({ name: '圖片.png', type: 'image/png' }, ['.png']) // true
|
|
253
274
|
* ```
|
|
254
275
|
*/ const validateFileType = (file, accepts)=>{
|
|
255
276
|
if (accepts.length === 0) return true;
|
|
256
277
|
// 獲取文件的MIME類型
|
|
257
278
|
const fileMimeType = file.type;
|
|
279
|
+
// 提取副檔名(含 .,且轉小寫)
|
|
280
|
+
const fileExt = file.name.includes('.') ? `.${file.name.split('.').pop().toLowerCase()}` : '';
|
|
258
281
|
return accepts.some((accept)=>{
|
|
282
|
+
if (accept.startsWith('.')) {
|
|
283
|
+
// 以副檔名檢查,忽略大小寫
|
|
284
|
+
return fileExt === accept.toLowerCase();
|
|
285
|
+
}
|
|
259
286
|
if (accept === fileMimeType) {
|
|
260
287
|
return true;
|
|
261
288
|
}
|
|
262
289
|
if (accept.endsWith('/*')) {
|
|
263
|
-
|
|
264
|
-
const fileCategory = fileMimeType.split('/')[0];
|
|
265
|
-
if (acceptedCategory === fileCategory) {
|
|
266
|
-
return true;
|
|
267
|
-
}
|
|
290
|
+
return accept.split('/')[0] === fileMimeType.split('/')[0];
|
|
268
291
|
}
|
|
269
292
|
return false;
|
|
270
293
|
});
|
|
@@ -287,24 +310,74 @@ function createEnumLikeObject(obj, name, scene) {
|
|
|
287
310
|
* getMimeType('txt') // 'text/plain'
|
|
288
311
|
* getMimeType('zip') // 'application/zip'
|
|
289
312
|
* getMimeType('mp4') // 'application/octet-stream'
|
|
313
|
+
* getMimeType('xls') // 'application/vnd.ms-excel'
|
|
314
|
+
* getMimeType('xlsx') // 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
315
|
+
* getMimeType('doc') // 'application/msword'
|
|
316
|
+
* getMimeType('docx') // 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
317
|
+
*
|
|
318
|
+
* @deprecated use `parseFileInfoFromFilename` instead
|
|
290
319
|
*/ const getMimeType = (fileName)=>{
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
320
|
+
const ext = (fileName.split('.').pop() || '').toLowerCase();
|
|
321
|
+
return MimeTypeMap[ext] ?? OtherMimeType;
|
|
322
|
+
};
|
|
323
|
+
/**
|
|
324
|
+
* 用來解析後端在 response header content-disposition 的內容
|
|
325
|
+
*
|
|
326
|
+
* 一般來說格式會有以下兩種
|
|
327
|
+
*
|
|
328
|
+
* - Content-Disposition: attachment; filename="file name.jpg"
|
|
329
|
+
* - Content-Disposition: attachment; filename*=UTF-8''file%20name2.jpg
|
|
330
|
+
*
|
|
331
|
+
* 如果格式正確就會取得檔案名稱(包含副檔名),優先取 filename* 內的內容
|
|
332
|
+
*
|
|
333
|
+
* @param disposition Content-Disposition
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
*
|
|
337
|
+
* parseFilenameFromDisposition('attachment; filename="file name1.jpg') // file name.jpg
|
|
338
|
+
* parseFilenameFromDisposition('attachment; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
|
|
339
|
+
* parseFilenameFromDisposition('attachment; filename="file name.jpg; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
|
|
340
|
+
*/ const parseFilenameFromDisposition = (disposition)=>{
|
|
341
|
+
// 1. 先找 filename*
|
|
342
|
+
const filenameStarMatch = disposition.match(/filename\*\s*=\s*(?:UTF-8'')?([^;]+)/i);
|
|
343
|
+
if (filenameStarMatch && filenameStarMatch[1]) {
|
|
344
|
+
// 依 RFC 5987 格式(UTF-8''URL-ENCODED),要先 decode
|
|
345
|
+
try {
|
|
346
|
+
return decodeURIComponent(filenameStarMatch[1].replace(/(^['"]|['"]$)/g, ''));
|
|
347
|
+
} catch {
|
|
348
|
+
// fallback,如果 decode 失敗,直接傳回原值
|
|
349
|
+
return filenameStarMatch[1];
|
|
350
|
+
}
|
|
307
351
|
}
|
|
352
|
+
// 2. 沒有 filename*,再找 filename
|
|
353
|
+
const filenameMatch = disposition.match(/filename\s*=\s*("?)([^";]+)\1/i);
|
|
354
|
+
if (filenameMatch && filenameMatch[2]) {
|
|
355
|
+
return filenameMatch[2];
|
|
356
|
+
}
|
|
357
|
+
// 3. 都沒有則 undefined
|
|
358
|
+
return undefined;
|
|
359
|
+
};
|
|
360
|
+
/**
|
|
361
|
+
* 解析 `filename` 回傳檔名、副檔名、MIME type
|
|
362
|
+
*
|
|
363
|
+
* @param filename 檔案名稱
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
*
|
|
367
|
+
* parseFileInfoFromFilename('image.jpg') // ['image', 'jpg', 'image/jpeg']
|
|
368
|
+
* parseFileInfoFromFilename('image') // ['image', '', 'application/octet-stream']
|
|
369
|
+
*/ const parseFileInfoFromFilename = (filename)=>{
|
|
370
|
+
const lastDot = filename.lastIndexOf('.');
|
|
371
|
+
if (lastDot === -1) return [
|
|
372
|
+
filename,
|
|
373
|
+
'',
|
|
374
|
+
OtherMimeType
|
|
375
|
+
]; // 沒有副檔名
|
|
376
|
+
return [
|
|
377
|
+
filename.slice(0, lastDot),
|
|
378
|
+
filename.slice(lastDot + 1),
|
|
379
|
+
MimeTypeMap[filename.slice(lastDot + 1)] ?? OtherMimeType
|
|
380
|
+
];
|
|
308
381
|
};
|
|
309
382
|
|
|
310
383
|
// const isProduction: boolean = process.env.NODE_ENV === 'production';
|
|
@@ -1226,6 +1299,8 @@ exports.downloadFile = downloadClient.downloadFile;
|
|
|
1226
1299
|
exports.getLocalStorage = webStorageClient.getLocalStorage;
|
|
1227
1300
|
exports.setLocalStorage = webStorageClient.setLocalStorage;
|
|
1228
1301
|
exports.ByteSize = ByteSize;
|
|
1302
|
+
exports.MimeTypeMap = MimeTypeMap;
|
|
1303
|
+
exports.OtherMimeType = OtherMimeType;
|
|
1229
1304
|
exports.adToRocEra = adToRocEra;
|
|
1230
1305
|
exports.camelCase2PascalCase = camelCase2PascalCase;
|
|
1231
1306
|
exports.camelCase2SnakeCase = camelCase2SnakeCase;
|
|
@@ -1266,6 +1341,8 @@ exports.mergeRefs = mergeRefs;
|
|
|
1266
1341
|
exports.objectToSearchParams = objectToSearchParams;
|
|
1267
1342
|
exports.omit = omit;
|
|
1268
1343
|
exports.omitByValue = omitByValue;
|
|
1344
|
+
exports.parseFileInfoFromFilename = parseFileInfoFromFilename;
|
|
1345
|
+
exports.parseFilenameFromDisposition = parseFilenameFromDisposition;
|
|
1269
1346
|
exports.pascalCase2CamelCase = pascalCase2CamelCase;
|
|
1270
1347
|
exports.pascalCase2SnakeCase = pascalCase2SnakeCase;
|
|
1271
1348
|
exports.pascalString2CamelString = pascalString2CamelString;
|
package/dist/es/index.d.mts
CHANGED
|
@@ -474,11 +474,33 @@ declare const fakeApi: <T>(returnValue: T, result?: boolean, time?: number) => P
|
|
|
474
474
|
message: string;
|
|
475
475
|
}>;
|
|
476
476
|
|
|
477
|
+
declare const MimeTypeMap: {
|
|
478
|
+
readonly jpeg: "image/jpeg";
|
|
479
|
+
readonly jpg: "image/jpeg";
|
|
480
|
+
readonly gif: "image/gif";
|
|
481
|
+
readonly png: "image/png";
|
|
482
|
+
readonly pdf: "application/pdf";
|
|
483
|
+
readonly zip: "application/zip";
|
|
484
|
+
readonly csv: "text/csv";
|
|
485
|
+
readonly ppt: "application/vnd.ms-powerpoint";
|
|
486
|
+
readonly pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
|
487
|
+
readonly xls: "application/vnd.ms-excel";
|
|
488
|
+
readonly xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
489
|
+
readonly doc: "application/msword";
|
|
490
|
+
readonly docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
491
|
+
readonly txt: "text/plain";
|
|
492
|
+
};
|
|
493
|
+
declare const OtherMimeType = "application/octet-stream";
|
|
494
|
+
type MimeTypeExtension = keyof typeof MimeTypeMap;
|
|
495
|
+
type MimeTypeValue = (typeof MimeTypeMap)[MimeTypeExtension] | typeof OtherMimeType;
|
|
496
|
+
|
|
477
497
|
/**
|
|
478
|
-
*
|
|
498
|
+
* 檢查檔案是否為合法的檔案類型
|
|
499
|
+
*
|
|
500
|
+
* `accepts` 可同時接受副檔名以及 MIME 類型
|
|
479
501
|
*
|
|
480
502
|
* @param file 檔案
|
|
481
|
-
* @param accepts
|
|
503
|
+
* @param accepts 允許的類型
|
|
482
504
|
*
|
|
483
505
|
* @example
|
|
484
506
|
*
|
|
@@ -486,6 +508,7 @@ declare const fakeApi: <T>(returnValue: T, result?: boolean, time?: number) => P
|
|
|
486
508
|
* validateFileType({ type: 'image/png' }, ['image/png', 'image/jpeg']) // true
|
|
487
509
|
* validateFileType({ type: 'image/png' }, ['image/jpeg']) // false
|
|
488
510
|
* validateFileType({ type: 'image/png' }, ['image/*']) // true
|
|
511
|
+
* validateFileType({ name: '圖片.png', type: 'image/png' }, ['.png']) // true
|
|
489
512
|
* ```
|
|
490
513
|
*/
|
|
491
514
|
declare const validateFileType: (file: File, accepts: string[]) => boolean;
|
|
@@ -507,8 +530,44 @@ declare const validateFileType: (file: File, accepts: string[]) => boolean;
|
|
|
507
530
|
* getMimeType('txt') // 'text/plain'
|
|
508
531
|
* getMimeType('zip') // 'application/zip'
|
|
509
532
|
* getMimeType('mp4') // 'application/octet-stream'
|
|
533
|
+
* getMimeType('xls') // 'application/vnd.ms-excel'
|
|
534
|
+
* getMimeType('xlsx') // 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
535
|
+
* getMimeType('doc') // 'application/msword'
|
|
536
|
+
* getMimeType('docx') // 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
537
|
+
*
|
|
538
|
+
* @deprecated use `parseFileInfoFromFilename` instead
|
|
539
|
+
*/
|
|
540
|
+
declare const getMimeType: (fileName: string) => "image/jpeg" | "image/gif" | "image/png" | "application/pdf" | "application/zip" | "text/csv" | "application/vnd.ms-powerpoint" | "application/vnd.openxmlformats-officedocument.presentationml.presentation" | "application/vnd.ms-excel" | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | "application/msword" | "application/vnd.openxmlformats-officedocument.wordprocessingml.document" | "text/plain";
|
|
541
|
+
/**
|
|
542
|
+
* 用來解析後端在 response header content-disposition 的內容
|
|
543
|
+
*
|
|
544
|
+
* 一般來說格式會有以下兩種
|
|
545
|
+
*
|
|
546
|
+
* - Content-Disposition: attachment; filename="file name.jpg"
|
|
547
|
+
* - Content-Disposition: attachment; filename*=UTF-8''file%20name2.jpg
|
|
548
|
+
*
|
|
549
|
+
* 如果格式正確就會取得檔案名稱(包含副檔名),優先取 filename* 內的內容
|
|
550
|
+
*
|
|
551
|
+
* @param disposition Content-Disposition
|
|
552
|
+
*
|
|
553
|
+
* @example
|
|
554
|
+
*
|
|
555
|
+
* parseFilenameFromDisposition('attachment; filename="file name1.jpg') // file name.jpg
|
|
556
|
+
* parseFilenameFromDisposition('attachment; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
|
|
557
|
+
* parseFilenameFromDisposition('attachment; filename="file name.jpg; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
|
|
558
|
+
*/
|
|
559
|
+
declare const parseFilenameFromDisposition: (disposition: string) => string | undefined;
|
|
560
|
+
/**
|
|
561
|
+
* 解析 `filename` 回傳檔名、副檔名、MIME type
|
|
562
|
+
*
|
|
563
|
+
* @param filename 檔案名稱
|
|
564
|
+
*
|
|
565
|
+
* @example
|
|
566
|
+
*
|
|
567
|
+
* parseFileInfoFromFilename('image.jpg') // ['image', 'jpg', 'image/jpeg']
|
|
568
|
+
* parseFileInfoFromFilename('image') // ['image', '', 'application/octet-stream']
|
|
510
569
|
*/
|
|
511
|
-
declare const
|
|
570
|
+
declare const parseFileInfoFromFilename: (filename: string) => [string, string, MimeTypeValue];
|
|
512
571
|
|
|
513
572
|
declare function invariant(condition: any, message?: string | (() => string)): asserts condition;
|
|
514
573
|
|
|
@@ -1145,4 +1204,4 @@ declare const getLocalStorage: <T>(key: string, deCode?: boolean) => T | undefin
|
|
|
1145
1204
|
*/
|
|
1146
1205
|
declare const setLocalStorage: (key: string, value: Record<string, any>, enCode?: boolean) => void;
|
|
1147
1206
|
|
|
1148
|
-
export { ByteSize, type PartialBy, QueryProvider, type RequiredBy, type TCountdownActions, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createDataContext, createEnumLikeObject, debounce, deepMerge, downloadFile, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getLocalStorage, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeRefs, objectToSearchParams, omit, omitByValue, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, setLocalStorage, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useCountdown, useQueryContext, useValue, validTaxId, validateDateString, validateFileType, wait };
|
|
1207
|
+
export { ByteSize, type MimeTypeExtension, MimeTypeMap, type MimeTypeValue, OtherMimeType, type PartialBy, QueryProvider, type RequiredBy, type TCountdownActions, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createDataContext, createEnumLikeObject, debounce, deepMerge, downloadFile, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getLocalStorage, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeRefs, objectToSearchParams, omit, omitByValue, parseFileInfoFromFilename, parseFilenameFromDisposition, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, setLocalStorage, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useCountdown, useQueryContext, useValue, validTaxId, validateDateString, validateFileType, wait };
|
package/dist/es/index.mjs
CHANGED
|
@@ -231,11 +231,31 @@ function createEnumLikeObject(obj, name, scene) {
|
|
|
231
231
|
}, time);
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
+
const MimeTypeMap = {
|
|
235
|
+
jpeg: 'image/jpeg',
|
|
236
|
+
jpg: 'image/jpeg',
|
|
237
|
+
gif: 'image/gif',
|
|
238
|
+
png: 'image/png',
|
|
239
|
+
pdf: 'application/pdf',
|
|
240
|
+
zip: 'application/zip',
|
|
241
|
+
csv: 'text/csv',
|
|
242
|
+
ppt: 'application/vnd.ms-powerpoint',
|
|
243
|
+
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
244
|
+
xls: 'application/vnd.ms-excel',
|
|
245
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
246
|
+
doc: 'application/msword',
|
|
247
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
248
|
+
txt: 'text/plain'
|
|
249
|
+
};
|
|
250
|
+
const OtherMimeType = 'application/octet-stream';
|
|
251
|
+
|
|
234
252
|
/**
|
|
235
|
-
*
|
|
253
|
+
* 檢查檔案是否為合法的檔案類型
|
|
254
|
+
*
|
|
255
|
+
* `accepts` 可同時接受副檔名以及 MIME 類型
|
|
236
256
|
*
|
|
237
257
|
* @param file 檔案
|
|
238
|
-
* @param accepts
|
|
258
|
+
* @param accepts 允許的類型
|
|
239
259
|
*
|
|
240
260
|
* @example
|
|
241
261
|
*
|
|
@@ -243,21 +263,24 @@ function createEnumLikeObject(obj, name, scene) {
|
|
|
243
263
|
* validateFileType({ type: 'image/png' }, ['image/png', 'image/jpeg']) // true
|
|
244
264
|
* validateFileType({ type: 'image/png' }, ['image/jpeg']) // false
|
|
245
265
|
* validateFileType({ type: 'image/png' }, ['image/*']) // true
|
|
266
|
+
* validateFileType({ name: '圖片.png', type: 'image/png' }, ['.png']) // true
|
|
246
267
|
* ```
|
|
247
268
|
*/ const validateFileType = (file, accepts)=>{
|
|
248
269
|
if (accepts.length === 0) return true;
|
|
249
270
|
// 獲取文件的MIME類型
|
|
250
271
|
const fileMimeType = file.type;
|
|
272
|
+
// 提取副檔名(含 .,且轉小寫)
|
|
273
|
+
const fileExt = file.name.includes('.') ? `.${file.name.split('.').pop().toLowerCase()}` : '';
|
|
251
274
|
return accepts.some((accept)=>{
|
|
275
|
+
if (accept.startsWith('.')) {
|
|
276
|
+
// 以副檔名檢查,忽略大小寫
|
|
277
|
+
return fileExt === accept.toLowerCase();
|
|
278
|
+
}
|
|
252
279
|
if (accept === fileMimeType) {
|
|
253
280
|
return true;
|
|
254
281
|
}
|
|
255
282
|
if (accept.endsWith('/*')) {
|
|
256
|
-
|
|
257
|
-
const fileCategory = fileMimeType.split('/')[0];
|
|
258
|
-
if (acceptedCategory === fileCategory) {
|
|
259
|
-
return true;
|
|
260
|
-
}
|
|
283
|
+
return accept.split('/')[0] === fileMimeType.split('/')[0];
|
|
261
284
|
}
|
|
262
285
|
return false;
|
|
263
286
|
});
|
|
@@ -280,24 +303,74 @@ function createEnumLikeObject(obj, name, scene) {
|
|
|
280
303
|
* getMimeType('txt') // 'text/plain'
|
|
281
304
|
* getMimeType('zip') // 'application/zip'
|
|
282
305
|
* getMimeType('mp4') // 'application/octet-stream'
|
|
306
|
+
* getMimeType('xls') // 'application/vnd.ms-excel'
|
|
307
|
+
* getMimeType('xlsx') // 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
308
|
+
* getMimeType('doc') // 'application/msword'
|
|
309
|
+
* getMimeType('docx') // 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
310
|
+
*
|
|
311
|
+
* @deprecated use `parseFileInfoFromFilename` instead
|
|
283
312
|
*/ const getMimeType = (fileName)=>{
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
313
|
+
const ext = (fileName.split('.').pop() || '').toLowerCase();
|
|
314
|
+
return MimeTypeMap[ext] ?? OtherMimeType;
|
|
315
|
+
};
|
|
316
|
+
/**
|
|
317
|
+
* 用來解析後端在 response header content-disposition 的內容
|
|
318
|
+
*
|
|
319
|
+
* 一般來說格式會有以下兩種
|
|
320
|
+
*
|
|
321
|
+
* - Content-Disposition: attachment; filename="file name.jpg"
|
|
322
|
+
* - Content-Disposition: attachment; filename*=UTF-8''file%20name2.jpg
|
|
323
|
+
*
|
|
324
|
+
* 如果格式正確就會取得檔案名稱(包含副檔名),優先取 filename* 內的內容
|
|
325
|
+
*
|
|
326
|
+
* @param disposition Content-Disposition
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
*
|
|
330
|
+
* parseFilenameFromDisposition('attachment; filename="file name1.jpg') // file name.jpg
|
|
331
|
+
* parseFilenameFromDisposition('attachment; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
|
|
332
|
+
* parseFilenameFromDisposition('attachment; filename="file name.jpg; filename*=UTF-8''file%20name2.jpg') // file name2.jpg
|
|
333
|
+
*/ const parseFilenameFromDisposition = (disposition)=>{
|
|
334
|
+
// 1. 先找 filename*
|
|
335
|
+
const filenameStarMatch = disposition.match(/filename\*\s*=\s*(?:UTF-8'')?([^;]+)/i);
|
|
336
|
+
if (filenameStarMatch && filenameStarMatch[1]) {
|
|
337
|
+
// 依 RFC 5987 格式(UTF-8''URL-ENCODED),要先 decode
|
|
338
|
+
try {
|
|
339
|
+
return decodeURIComponent(filenameStarMatch[1].replace(/(^['"]|['"]$)/g, ''));
|
|
340
|
+
} catch {
|
|
341
|
+
// fallback,如果 decode 失敗,直接傳回原值
|
|
342
|
+
return filenameStarMatch[1];
|
|
343
|
+
}
|
|
300
344
|
}
|
|
345
|
+
// 2. 沒有 filename*,再找 filename
|
|
346
|
+
const filenameMatch = disposition.match(/filename\s*=\s*("?)([^";]+)\1/i);
|
|
347
|
+
if (filenameMatch && filenameMatch[2]) {
|
|
348
|
+
return filenameMatch[2];
|
|
349
|
+
}
|
|
350
|
+
// 3. 都沒有則 undefined
|
|
351
|
+
return undefined;
|
|
352
|
+
};
|
|
353
|
+
/**
|
|
354
|
+
* 解析 `filename` 回傳檔名、副檔名、MIME type
|
|
355
|
+
*
|
|
356
|
+
* @param filename 檔案名稱
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
*
|
|
360
|
+
* parseFileInfoFromFilename('image.jpg') // ['image', 'jpg', 'image/jpeg']
|
|
361
|
+
* parseFileInfoFromFilename('image') // ['image', '', 'application/octet-stream']
|
|
362
|
+
*/ const parseFileInfoFromFilename = (filename)=>{
|
|
363
|
+
const lastDot = filename.lastIndexOf('.');
|
|
364
|
+
if (lastDot === -1) return [
|
|
365
|
+
filename,
|
|
366
|
+
'',
|
|
367
|
+
OtherMimeType
|
|
368
|
+
]; // 沒有副檔名
|
|
369
|
+
return [
|
|
370
|
+
filename.slice(0, lastDot),
|
|
371
|
+
filename.slice(lastDot + 1),
|
|
372
|
+
MimeTypeMap[filename.slice(lastDot + 1)] ?? OtherMimeType
|
|
373
|
+
];
|
|
301
374
|
};
|
|
302
375
|
|
|
303
376
|
// const isProduction: boolean = process.env.NODE_ENV === 'production';
|
|
@@ -1212,4 +1285,4 @@ function mergeRefs(refs) {
|
|
|
1212
1285
|
return dayjs(endMonth, 'YYYYMM').subtract(1911, 'year').format('YYYYMM').substring(1);
|
|
1213
1286
|
};
|
|
1214
1287
|
|
|
1215
|
-
export { ByteSize, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createDataContext, createEnumLikeObject, debounce, deepMerge, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeRefs, objectToSearchParams, omit, omitByValue, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useValue, validTaxId, validateDateString, validateFileType, wait };
|
|
1288
|
+
export { ByteSize, MimeTypeMap, OtherMimeType, adToRocEra, camelCase2PascalCase, camelCase2SnakeCase, camelString2PascalString, camelString2SnakeString, convertBytes, createDataContext, createEnumLikeObject, debounce, deepMerge, extractEnumLikeObject, fakeApi, formatAmount, formatBytes, formatStarMask, generatePeriodArray, getCurrentPeriod, getMimeType, invariant, isChinese, isDateString, isDateTimeString, isEmail, isEnglish, isEqual, isNonZeroStart, isNumber, isNumberAtLeastN, isNumberN, isNumberNM, isServer, isTWMobile, isTWPhone, isTimeString, isValidPassword, maskString, mergeRefs, objectToSearchParams, omit, omitByValue, parseFileInfoFromFilename, parseFilenameFromDisposition, pascalCase2CamelCase, pascalCase2SnakeCase, pascalString2CamelString, pascalString2SnakeString, pick, pickByValue, rocEraToAd, searchParamsToObject, snakeCase2CamelCase, snakeCase2PascalCase, snakeString2CamelString, snakeString2PascalString, throttle, useValue, validTaxId, validateDateString, validateFileType, wait };
|