/** * 分片上传配置 * 按网络环境选择分片大小和并发数。 * * 策略: * - 内网:20MB 分片,2 并发 * - 公网:10MB 分片,1 并发 * - 公网链路经过 nginx + FRP,过大的单请求会放大连接抖动和 499/502 风险 */ import { getNetworkStatus, initializeNetwork } from './network' /** * 获取最优分片大小 * @returns 分片大小(字节) */ export async function getOptimalChunkSize(): Promise { let network = getNetworkStatus() if (!network.lastChecked) { try { network = await initializeNetwork() } catch (error) { console.warn('[ChunkConfig] 网络检测失败,使用公网保守分片(10MB):', error) return 10 * 1024 * 1024 } } const chunkSize = network.isInternal ? 20 * 1024 * 1024 : 10 * 1024 * 1024 console.log(`[ChunkConfig] 检测到${network.isInternal ? '内网' : '公网'}访问,使用 ${formatChunkSize(chunkSize)} 分片`) return chunkSize } /** * 同步获取分片大小 * @returns 分片大小(字节) */ export function getChunkSizeSync(): number { const network = getNetworkStatus() const chunkSize = network.isInternal ? 20 * 1024 * 1024 : 10 * 1024 * 1024 console.log(`[ChunkConfig] 使用 ${formatChunkSize(chunkSize)} 分片(同步)`) return chunkSize } /** * 获取超时时间(根据分片大小动态调整) * 假设最低速度 300KB/s,加上100%缓冲(更保守) * @param chunkSize 分片大小(字节) * @returns 超时时间(毫秒) */ export function getChunkTimeout(chunkSize: number): number { const minSpeed = 300 * 1024 // 300KB/s(更保守的最低速度) const baseTimeout = (chunkSize / minSpeed) * 1000 // 加上100%缓冲,确保不会轻易超时 const timeout = Math.ceil(baseTimeout * 2) // 最小5分钟,最大10分钟 return Math.max(300000, Math.min(600000, timeout)) } /** * 获取分片大小的可读字符串 * @param chunkSize 分片大小(字节) * @returns 可读字符串(如 "10MB") */ export function formatChunkSize(chunkSize: number): string { return `${(chunkSize / 1024 / 1024).toFixed(0)}MB` } /** * 获取最优并发数(根据网络环境和系统负载动态调整) * * 动态调整策略: * - 内网用户:2 并发 * - 公网用户:1 并发 * - 公网链路经过 FRP,优先减少大请求并发,避免上传到 100% 后等待/重试过久 * * @returns 并发数(1-2) */ export async function getOptimalConcurrency(): Promise { // 获取当前网络状态 let network = getNetworkStatus() // 如果还没检测过,先初始化网络检测 if (!network.lastChecked) { try { network = await initializeNetwork() } catch (error) { console.warn('[ChunkConfig] 网络检测失败,使用默认并发数(2):', error) return 2 } } // 基础并发数 let concurrency: number if (network.isInternal) { // 内网用户:2并发 concurrency = 2 } else { // 公网用户:1并发 concurrency = 1 } // 确保在1-2范围内 concurrency = Math.max(1, Math.min(2, concurrency)) console.log(`[ChunkConfig] 检测到${network.isInternal ? '内网' : '公网'}访问,使用 ${concurrency} 个并发(1~2范围)`) return concurrency } /** * 同步获取并发数(如果网络状态已缓存) * 如果未检测过,返回默认值(2并发) * @returns 并发数(1-3) */ export function getConcurrencySync(): number { const network = getNetworkStatus() // 如果还没检测过,返回公网保守值(1并发) if (!network.lastChecked) { console.warn('[ChunkConfig] 网络状态未检测,使用默认并发数(1)') return 1 } // 同步版本:使用固定策略(不依赖异步检测) let concurrency: number if (network.isInternal) { concurrency = 2 // 内网:2并发 } else { concurrency = 1 // 公网:1并发 } // 确保在1-2范围内 concurrency = Math.max(1, Math.min(2, concurrency)) return concurrency } /** * 根据实际上传速度动态调整并发数 * * 调整策略: * - 速度 > 5MB/s:降低并发(避免浪费资源) * - 速度 2-5MB/s:保持当前并发 * - 速度 < 2MB/s:增加并发(最高3) * * @param currentConcurrency 当前并发数 * @param currentSpeed 当前上传速度(字节/秒) * @returns 调整后的并发数(1-3) */ export function adjustConcurrencyBySpeed(currentConcurrency: number, currentSpeed: number): number { const speedMBps = currentSpeed / (1024 * 1024) let newConcurrency = currentConcurrency if (speedMBps > 5) { // 速度很快,降低并发(节省资源) newConcurrency = Math.max(1, currentConcurrency - 1) } else if (speedMBps < 2 && currentConcurrency < 3) { // 速度慢,增加并发(最多3,避免服务器过载) newConcurrency = Math.min(3, currentConcurrency + 1) } if (newConcurrency !== currentConcurrency) { console.log(`[ChunkConfig] 动态调整并发: ${currentConcurrency} → ${newConcurrency} (当前速度: ${speedMBps.toFixed(2)}MB/s)`) } return newConcurrency } /** * 获取并发数的可读字符串 * @param concurrency 并发数 * @returns 可读字符串(如 "2个并发") */ export function formatConcurrency(concurrency: number): string { return `${concurrency}个并发` }