fix: stabilize public chunk uploads
This commit is contained in:
parent
8dd8ebac7e
commit
32f39f07bb
@ -921,7 +921,7 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) {
|
||||
})
|
||||
},
|
||||
skipComplete: true,
|
||||
maxRetries: Infinity,
|
||||
maxRetries: 5,
|
||||
})
|
||||
|
||||
totalUploadedBytes += item.file.size
|
||||
|
||||
@ -65,7 +65,7 @@ export async function uploadBatchFile(
|
||||
onProgress,
|
||||
onRequest,
|
||||
skipComplete: true,
|
||||
maxRetries: Infinity,
|
||||
maxRetries: 5,
|
||||
})
|
||||
|
||||
onStatusUpdate({ status: 'processing', progress: 100, uploadId: result?.uploadId })
|
||||
|
||||
@ -215,7 +215,7 @@ export async function uploadBatchMusic(
|
||||
onProgress(totalProgress)
|
||||
},
|
||||
skipComplete: true,
|
||||
maxRetries: Infinity,
|
||||
maxRetries: 5,
|
||||
})
|
||||
|
||||
// 等待completeBatchUpload完成,确保素材记录被创建
|
||||
@ -289,7 +289,7 @@ export async function uploadBatchVideo(
|
||||
onProgress(totalProgress)
|
||||
},
|
||||
skipComplete: true,
|
||||
maxRetries: Infinity,
|
||||
maxRetries: 5,
|
||||
})
|
||||
|
||||
// 等待completeBatchUpload完成,确保素材记录被创建
|
||||
@ -368,7 +368,7 @@ export async function uploadBatch3D(
|
||||
onProgress(totalProgress)
|
||||
},
|
||||
skipComplete: true,
|
||||
maxRetries: Infinity,
|
||||
maxRetries: 5,
|
||||
})
|
||||
|
||||
if (result?.uploadId) {
|
||||
@ -615,7 +615,7 @@ export async function uploadBatchBrandVI(
|
||||
onProgress(totalProgress)
|
||||
},
|
||||
skipComplete: true,
|
||||
maxRetries: Infinity,
|
||||
maxRetries: 5,
|
||||
})
|
||||
|
||||
// 等待completeBatchUpload完成,确保素材记录被创建
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
- 首页自动切到 `192.168.58.20:4320` 的逻辑已经下线。
|
||||
- `180.97.221.140:2320` 不再作为正式入口链路的一部分。
|
||||
- `sc.jiansun.vip` 的 nginx 必须放开大文件上传:`client_max_body_size 2048m`,并设置较长 `proxy_read_timeout` / `proxy_send_timeout`。2026-05-07 曾因该配置缺失导致 `/api/materials/upload/chunk` 50MB 分片被 nginx 直接返回 `413`,表现为素材上传一直卡住。
|
||||
- 当前前端分片策略是内网 20MB / 2 并发,公网 10MB / 1 并发;公网不要再默认使用 50MB 大分片,否则会放大 nginx + FRP 链路抖动下的 `499` / `502` 和上传 100% 后长时间等待问题。
|
||||
|
||||
### 2.2 资源 / S3
|
||||
|
||||
|
||||
@ -246,7 +246,7 @@ export function useBatchFilesUpload(
|
||||
})
|
||||
},
|
||||
skipComplete: true,
|
||||
maxRetries: Infinity,
|
||||
maxRetries: 5,
|
||||
})
|
||||
|
||||
totalUploadedBytes += item.file.size
|
||||
|
||||
@ -1,44 +1,50 @@
|
||||
/**
|
||||
* 分片上传配置
|
||||
* 统一使用50MB大分片,提升上传效率
|
||||
* 按网络环境选择分片大小和并发数。
|
||||
*
|
||||
* 策略:
|
||||
* - 统一使用50MB分片(内网公网一致)
|
||||
* - 请求数减少80%(1GB文件从100个减少到20个)
|
||||
* - TCP利用率更高(单个分片传输时间更长)
|
||||
*
|
||||
* 优势:
|
||||
* - 减少网络开销(请求数少)
|
||||
* - 提升传输效率(TCP慢启动影响更小)
|
||||
* - 降低服务器压力(并发处理更少)
|
||||
* - 内网:20MB 分片,2 并发
|
||||
* - 公网:10MB 分片,1 并发
|
||||
* - 公网链路经过 nginx + FRP,过大的单请求会放大连接抖动和 499/502 风险
|
||||
*/
|
||||
|
||||
import { getNetworkStatus, initializeNetwork } from './network'
|
||||
|
||||
/**
|
||||
* 获取最优分片大小(统一50MB)
|
||||
* 获取最优分片大小
|
||||
* @returns 分片大小(字节)
|
||||
*/
|
||||
export async function getOptimalChunkSize(): Promise<number> {
|
||||
// 统一使用50MB分片(内网公网一致)
|
||||
// 优势:
|
||||
// 1. 减少请求数(1GB文件从100个减少到20个)
|
||||
// 2. TCP利用率更高(单个分片传输时间更长)
|
||||
// 3. 降低网络开销
|
||||
const chunkSize = 50 * 1024 * 1024 // 50MB
|
||||
let network = getNetworkStatus()
|
||||
|
||||
console.log(`[ChunkConfig] 使用 50MB 分片(统一配置)`)
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步获取分片大小(统一50MB)
|
||||
* 同步获取分片大小
|
||||
* @returns 分片大小(字节)
|
||||
*/
|
||||
export function getChunkSizeSync(): number {
|
||||
const chunkSize = 50 * 1024 * 1024 // 50MB
|
||||
console.log(`[ChunkConfig] 使用 50MB 分片(同步)`)
|
||||
const network = getNetworkStatus()
|
||||
const chunkSize = network.isInternal
|
||||
? 20 * 1024 * 1024
|
||||
: 10 * 1024 * 1024
|
||||
console.log(`[ChunkConfig] 使用 ${formatChunkSize(chunkSize)} 分片(同步)`)
|
||||
return chunkSize
|
||||
}
|
||||
|
||||
@ -55,7 +61,7 @@ export function getChunkTimeout(chunkSize: number): number {
|
||||
// 加上100%缓冲,确保不会轻易超时
|
||||
const timeout = Math.ceil(baseTimeout * 2)
|
||||
|
||||
// 最小5分钟,最大10分钟(50MB分片在300KB/s下需要约5.5分钟)
|
||||
// 最小5分钟,最大10分钟
|
||||
return Math.max(300000, Math.min(600000, timeout))
|
||||
}
|
||||
|
||||
@ -71,12 +77,12 @@ export function formatChunkSize(chunkSize: number): string {
|
||||
/**
|
||||
* 获取最优并发数(根据网络环境和系统负载动态调整)
|
||||
*
|
||||
* 动态调整策略(1~3并发):
|
||||
* - 内网用户:2-3并发
|
||||
* - 公网用户:2并发
|
||||
* - 原因:现在已配置 proxyClientMaxBodySize = 100MB,可以处理 50MB 分片
|
||||
* 动态调整策略:
|
||||
* - 内网用户:2 并发
|
||||
* - 公网用户:1 并发
|
||||
* - 公网链路经过 FRP,优先减少大请求并发,避免上传到 100% 后等待/重试过久
|
||||
*
|
||||
* @returns 并发数(1-3)
|
||||
* @returns 并发数(1-2)
|
||||
*/
|
||||
export async function getOptimalConcurrency(): Promise<number> {
|
||||
// 获取当前网络状态
|
||||
@ -96,17 +102,17 @@ export async function getOptimalConcurrency(): Promise<number> {
|
||||
let concurrency: number
|
||||
|
||||
if (network.isInternal) {
|
||||
// 内网用户:3并发
|
||||
concurrency = 3
|
||||
} else {
|
||||
// 公网用户:2并发
|
||||
// 内网用户:2并发
|
||||
concurrency = 2
|
||||
} else {
|
||||
// 公网用户:1并发
|
||||
concurrency = 1
|
||||
}
|
||||
|
||||
// 确保在1-3范围内
|
||||
concurrency = Math.max(1, Math.min(3, concurrency))
|
||||
// 确保在1-2范围内
|
||||
concurrency = Math.max(1, Math.min(2, concurrency))
|
||||
|
||||
console.log(`[ChunkConfig] 检测到${network.isInternal ? '内网' : '公网'}访问,使用 ${concurrency} 个并发(1~3范围)`)
|
||||
console.log(`[ChunkConfig] 检测到${network.isInternal ? '内网' : '公网'}访问,使用 ${concurrency} 个并发(1~2范围)`)
|
||||
|
||||
return concurrency
|
||||
}
|
||||
@ -119,23 +125,23 @@ export async function getOptimalConcurrency(): Promise<number> {
|
||||
export function getConcurrencySync(): number {
|
||||
const network = getNetworkStatus()
|
||||
|
||||
// 如果还没检测过,返回保守值(2并发)
|
||||
// 如果还没检测过,返回公网保守值(1并发)
|
||||
if (!network.lastChecked) {
|
||||
console.warn('[ChunkConfig] 网络状态未检测,使用默认并发数(2)')
|
||||
return 2
|
||||
console.warn('[ChunkConfig] 网络状态未检测,使用默认并发数(1)')
|
||||
return 1
|
||||
}
|
||||
|
||||
// 同步版本:使用固定策略(不依赖异步检测)
|
||||
let concurrency: number
|
||||
|
||||
if (network.isInternal) {
|
||||
concurrency = 3 // 内网:3并发
|
||||
concurrency = 2 // 内网:2并发
|
||||
} else {
|
||||
concurrency = 2 // 公网:2并发
|
||||
concurrency = 1 // 公网:1并发
|
||||
}
|
||||
|
||||
// 确保在1-3范围内
|
||||
concurrency = Math.max(1, Math.min(3, concurrency))
|
||||
// 确保在1-2范围内
|
||||
concurrency = Math.max(1, Math.min(2, concurrency))
|
||||
|
||||
return concurrency
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ interface ChunkUploadOptions {
|
||||
chunkSize?: number // 每块大小(字节),默认根据网络环境自动选择(内网20MB,公网10MB)
|
||||
onProgress?: (progress: number, speed?: number) => void // progress: 0-100, speed: 字节/秒
|
||||
onError?: (error: Error) => void
|
||||
maxRetries?: number // 最大重试次数,默认无限重试
|
||||
maxRetries?: number // 最大重试次数,默认5次
|
||||
retryDelay?: number // 重试延迟(毫秒),默认1000ms
|
||||
onStageChange?: (
|
||||
stage: 'uploading' | 'processing',
|
||||
@ -383,7 +383,7 @@ export async function chunkUpload(
|
||||
const {
|
||||
chunkSize = defaultChunkSize, // 根据网络环境自动选择(内网20MB,公网10MB)
|
||||
onProgress,
|
||||
maxRetries = Infinity, // 默认无限重试
|
||||
maxRetries = 5,
|
||||
retryDelay = 1000,
|
||||
onStageChange,
|
||||
onRequest,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user