327 lines
6.3 KiB
JavaScript
327 lines
6.3 KiB
JavaScript
const crypto = require('crypto');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
/**
|
||
* 生成随机字符串
|
||
*/
|
||
function generateRandomString(length = 32, charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') {
|
||
let result = '';
|
||
for (let i = 0; i < length; i++) {
|
||
result += charset.charAt(Math.floor(Math.random() * charset.length));
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 生成UUID
|
||
*/
|
||
function generateUUID() {
|
||
return crypto.randomUUID();
|
||
}
|
||
|
||
/**
|
||
* 生成MD5哈希
|
||
*/
|
||
function generateMD5(data) {
|
||
return crypto.createHash('md5').update(data).digest('hex');
|
||
}
|
||
|
||
/**
|
||
* 生成SHA256哈希
|
||
*/
|
||
function generateSHA256(data) {
|
||
return crypto.createHash('sha256').update(data).digest('hex');
|
||
}
|
||
|
||
/**
|
||
* 延迟函数
|
||
*/
|
||
function delay(ms) {
|
||
return new Promise(resolve => setTimeout(resolve, ms));
|
||
}
|
||
|
||
/**
|
||
* 深度克隆对象
|
||
*/
|
||
function deepClone(obj) {
|
||
if (obj === null || typeof obj !== 'object') {
|
||
return obj;
|
||
}
|
||
|
||
if (obj instanceof Date) {
|
||
return new Date(obj.getTime());
|
||
}
|
||
|
||
if (obj instanceof Array) {
|
||
return obj.map(item => deepClone(item));
|
||
}
|
||
|
||
if (typeof obj === 'object') {
|
||
const clonedObj = {};
|
||
for (const key in obj) {
|
||
if (obj.hasOwnProperty(key)) {
|
||
clonedObj[key] = deepClone(obj[key]);
|
||
}
|
||
}
|
||
return clonedObj;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 格式化文件大小
|
||
*/
|
||
function formatFileSize(bytes) {
|
||
if (bytes === 0) return '0 Bytes';
|
||
|
||
const k = 1024;
|
||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||
|
||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||
}
|
||
|
||
/**
|
||
* 格式化时间差
|
||
*/
|
||
function formatTimeDiff(startTime, endTime = Date.now()) {
|
||
const diff = endTime - startTime;
|
||
const seconds = Math.floor(diff / 1000);
|
||
const minutes = Math.floor(seconds / 60);
|
||
const hours = Math.floor(minutes / 60);
|
||
const days = Math.floor(hours / 24);
|
||
|
||
if (days > 0) return `${days}天${hours % 24}小时`;
|
||
if (hours > 0) return `${hours}小时${minutes % 60}分钟`;
|
||
if (minutes > 0) return `${minutes}分钟${seconds % 60}秒`;
|
||
return `${seconds}秒`;
|
||
}
|
||
|
||
/**
|
||
* 验证邮箱格式
|
||
*/
|
||
function isValidEmail(email) {
|
||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||
return emailRegex.test(email);
|
||
}
|
||
|
||
/**
|
||
* 验证URL格式
|
||
*/
|
||
function isValidURL(url) {
|
||
try {
|
||
new URL(url);
|
||
return true;
|
||
} catch {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清理字符串(移除HTML标签和特殊字符)
|
||
*/
|
||
function sanitizeString(str) {
|
||
if (typeof str !== 'string') return '';
|
||
|
||
return str
|
||
.replace(/<[^>]*>/g, '') // 移除HTML标签
|
||
.replace(/[<>'"&]/g, '') // 移除危险字符
|
||
.trim();
|
||
}
|
||
|
||
/**
|
||
* 截断字符串
|
||
*/
|
||
function truncateString(str, maxLength, suffix = '...') {
|
||
if (typeof str !== 'string') return '';
|
||
|
||
if (str.length <= maxLength) return str;
|
||
|
||
return str.substring(0, maxLength - suffix.length) + suffix;
|
||
}
|
||
|
||
/**
|
||
* 检查文件是否存在
|
||
*/
|
||
function fileExists(filePath) {
|
||
try {
|
||
return fs.existsSync(filePath);
|
||
} catch {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 确保目录存在
|
||
*/
|
||
function ensureDirectoryExists(dirPath) {
|
||
try {
|
||
if (!fs.existsSync(dirPath)) {
|
||
fs.mkdirSync(dirPath, { recursive: true });
|
||
}
|
||
return true;
|
||
} catch (error) {
|
||
console.error('创建目录失败:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 读取JSON文件
|
||
*/
|
||
function readJSONFile(filePath) {
|
||
try {
|
||
const data = fs.readFileSync(filePath, 'utf8');
|
||
return JSON.parse(data);
|
||
} catch (error) {
|
||
console.error('读取JSON文件失败:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 写入JSON文件
|
||
*/
|
||
function writeJSONFile(filePath, data) {
|
||
try {
|
||
const jsonString = JSON.stringify(data, null, 2);
|
||
fs.writeFileSync(filePath, jsonString, 'utf8');
|
||
return true;
|
||
} catch (error) {
|
||
console.error('写入JSON文件失败:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取文件扩展名
|
||
*/
|
||
function getFileExtension(filename) {
|
||
return path.extname(filename).toLowerCase();
|
||
}
|
||
|
||
/**
|
||
* 获取文件名(不含扩展名)
|
||
*/
|
||
function getFileNameWithoutExtension(filename) {
|
||
return path.basename(filename, path.extname(filename));
|
||
}
|
||
|
||
/**
|
||
* 转换为驼峰命名
|
||
*/
|
||
function toCamelCase(str) {
|
||
return str.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase());
|
||
}
|
||
|
||
/**
|
||
* 转换为kebab命名
|
||
*/
|
||
function toKebabCase(str) {
|
||
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
||
}
|
||
|
||
/**
|
||
* 防抖函数
|
||
*/
|
||
function debounce(func, wait) {
|
||
let timeout;
|
||
return function executedFunction(...args) {
|
||
const later = () => {
|
||
clearTimeout(timeout);
|
||
func(...args);
|
||
};
|
||
clearTimeout(timeout);
|
||
timeout = setTimeout(later, wait);
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 节流函数
|
||
*/
|
||
function throttle(func, limit) {
|
||
let inThrottle;
|
||
return function executedFunction(...args) {
|
||
if (!inThrottle) {
|
||
func.apply(this, args);
|
||
inThrottle = true;
|
||
setTimeout(() => inThrottle = false, limit);
|
||
}
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 重试函数
|
||
*/
|
||
async function retry(fn, maxAttempts = 3, delay = 1000) {
|
||
let lastError;
|
||
|
||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||
try {
|
||
return await fn();
|
||
} catch (error) {
|
||
lastError = error;
|
||
|
||
if (attempt === maxAttempts) {
|
||
throw lastError;
|
||
}
|
||
|
||
console.warn(`尝试 ${attempt} 失败,${delay}ms后重试:`, error.message);
|
||
await new Promise(resolve => setTimeout(resolve, delay));
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 并行执行函数并限制并发数
|
||
*/
|
||
async function parallelLimit(tasks, limit = 5) {
|
||
const results = [];
|
||
const executing = [];
|
||
|
||
for (const task of tasks) {
|
||
const promise = Promise.resolve().then(() => task()).then(result => {
|
||
executing.splice(executing.indexOf(promise), 1);
|
||
return result;
|
||
});
|
||
|
||
results.push(promise);
|
||
|
||
if (results.length >= limit) {
|
||
executing.push(promise);
|
||
|
||
if (executing.length >= limit) {
|
||
await Promise.race(executing);
|
||
}
|
||
}
|
||
}
|
||
|
||
return Promise.all(results);
|
||
}
|
||
|
||
module.exports = {
|
||
generateRandomString,
|
||
generateUUID,
|
||
generateMD5,
|
||
generateSHA256,
|
||
delay,
|
||
deepClone,
|
||
formatFileSize,
|
||
formatTimeDiff,
|
||
isValidEmail,
|
||
isValidURL,
|
||
sanitizeString,
|
||
truncateString,
|
||
fileExists,
|
||
ensureDirectoryExists,
|
||
readJSONFile,
|
||
writeJSONFile,
|
||
getFileExtension,
|
||
getFileNameWithoutExtension,
|
||
toCamelCase,
|
||
toKebabCase,
|
||
debounce,
|
||
throttle,
|
||
retry,
|
||
parallelLimit
|
||
}; |