diff --git a/lib/background-uploader/automatic.ts b/lib/background-uploader/automatic.ts index 764ebef..ddaecc3 100644 --- a/lib/background-uploader/automatic.ts +++ b/lib/background-uploader/automatic.ts @@ -1,11 +1,15 @@ import * as BackgroundTask from 'expo-background-task'; import * as TaskManager from 'expo-task-manager'; +import pLimit from 'p-limit'; import { getUploadTaskStatus, initUploadTable, insertUploadTask } from '../db'; import { getMediaByDateRange } from './media'; import { processAndUploadMedia } from './uploader'; const BACKGROUND_UPLOAD_TASK = 'background-upload-task'; +const CONCURRENCY_LIMIT = 3; // 后台上传并发数,例如同时上传3个文件 +const limit = pLimit(CONCURRENCY_LIMIT); + // 注册后台任务 export const registerBackgroundUploadTask = async () => { try { @@ -45,38 +49,48 @@ TaskManager.defineTask(BACKGROUND_UPLOAD_TASK, async () => { console.log(`Found ${media.length} media files to potentially upload.`); - // 串行上传文件 + // 并发上传文件 let successCount = 0; let skippedCount = 0; let failedCount = 0; - for (const file of media) { - try { - const existingTask = await getUploadTaskStatus(file.uri); - if (!existingTask) { - // If not in DB, insert as pending - await insertUploadTask(file.uri, file.filename); - } else if (existingTask.status === 'success' || existingTask.status === 'skipped') { - console.log(`File ${file.uri} already ${existingTask.status}, skipping processing.`); - skippedCount++; - continue; // Skip processing if already successful or skipped - } + const uploadPromises = media.map((file) => + limit(async () => { + try { + const existingTask = await getUploadTaskStatus(file.uri); + if (!existingTask) { + await insertUploadTask(file.uri, file.filename); + } else if (existingTask.status === 'success' || existingTask.status === 'skipped') { + console.log(`File ${file.uri} already ${existingTask.status}, skipping processing.`); + return { status: 'skipped' }; // 返回状态以便统计 + } - const result = await processAndUploadMedia(file); - if (result === null) { - // Skipped by processAndUploadMedia (e.g., already uploaded) - skippedCount++; - } else if (result.originalSuccess) { - successCount++; - } else { - // Failed - failedCount++; + const result = await processAndUploadMedia(file); + if (result === null) { + return { status: 'skipped' }; + } else if (result.originalSuccess) { + return { status: 'success' }; + } else { + return { status: 'failed' }; + } + } catch (e) { + console.error('Upload failed for', file.uri, e); + return { status: 'failed' }; } - } catch (e) { - console.error('Upload failed for', file.uri, e); + }) + ); + + const results = await Promise.all(uploadPromises); + + results.forEach(result => { + if (result.status === 'success') { + successCount++; + } else if (result.status === 'skipped') { + skippedCount++; + } else if (result.status === 'failed') { failedCount++; } - } + }); console.log(`Background upload task finished. Successful: ${successCount}, Skipped: ${skippedCount}, Failed: ${failedCount}, Total: ${media.length}`); diff --git a/package.json b/package.json index 18d11ef..6013e21 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@react-navigation/elements": "^2.3.8", "@react-navigation/native": "^7.1.6", "@reduxjs/toolkit": "^2.8.2", + "@types/p-limit": "^2.2.0", "@types/react-redux": "^7.1.34", "expo": "~53.0.12", "expo-audio": "~0.4.7", @@ -53,6 +54,7 @@ "i18next-http-backend": "^3.0.2", "lottie-react-native": "7.2.2", "nativewind": "^4.1.23", + "p-limit": "^6.2.0", "react": "19.0.0", "react-dom": "19.0.0", "react-i18next": "^15.5.3",