Junhui Chen d31b587330 feat: 自动上传组件
feat: 自动上传组件
Co-authored-by: Junhui Chen <chenjunhui@fairclip.cn>
Co-committed-by: Junhui Chen <chenjunhui@fairclip.cn>
2025-07-17 15:55:27 +08:00

123 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as BackgroundTask from 'expo-background-task';
import * as TaskManager from 'expo-task-manager';
import pLimit from 'p-limit';
import { filterExistingFiles, initUploadTable, insertUploadTask, setAppState, updateUploadTaskStatus } from '../db';
import { getMediaByDateRange } from './media';
import { processAndUploadMedia } from './uploader';
const BACKGROUND_UPLOAD_TASK = 'background-upload-task';
const CONCURRENCY_LIMIT = 1; // 后台上传并发数例如同时上传3个文件
const limit = pLimit(CONCURRENCY_LIMIT);
// 注册后台任务
export async function registerBackgroundUploadTask() {
try {
// 初始化数据库表
await initUploadTable();
const isRegistered = await TaskManager.isTaskRegisteredAsync(BACKGROUND_UPLOAD_TASK);
if (isRegistered) {
console.log('Background task already registered.');
} else {
await BackgroundTask.registerTaskAsync(BACKGROUND_UPLOAD_TASK, {
minimumInterval: 15 * 60, // 15 分钟
});
console.log('Background task registered successfully.');
}
return true;
} catch (error) {
console.error('Error registering background task:', error);
return false;
}
};
// 触发手动或后台上传的通用函数
export async function triggerManualUpload(startDate: Date, endDate: Date): Promise<string | null> {
try {
console.log(`Triggering upload for range: ${startDate.toISOString()} to ${endDate.toISOString()}`);
const allMedia = await getMediaByDateRange(startDate, endDate);
if (allMedia.length === 0) {
console.log('No media files found in the specified range.');
return null;
}
// 1. Batch filter out files that have already been successfully uploaded.
const allFileUris = allMedia.map(a => a.uri);
const newFileUris = await filterExistingFiles(allFileUris);
if (newFileUris.length === 0) {
console.log('No new files to upload. All are already synced.');
return null;
}
const filesToUpload = allMedia.filter(a => newFileUris.includes(a.uri));
// 2. Batch pre-register all new files in the database as 'pending'.
console.log(`Registering ${filesToUpload.length} new files as 'pending' in the database.`);
for (const file of filesToUpload) {
await insertUploadTask({ uri: file.uri, filename: file.filename, status: 'pending', progress: 0 });
}
// 3. Start the upload session.
const startTime = Math.floor(Date.now() / 1000).toString();
await setAppState('uploadSessionStartTime', startTime);
// 4. Create upload promises for the new files.
const uploadPromises = filesToUpload.map((file) =>
limit(async () => {
try {
// 5. Mark the task as 'uploading' right before the upload starts.
await updateUploadTaskStatus(file.uri, 'uploading');
const result = await processAndUploadMedia(file);
if (result === null) { // Skipped case
await updateUploadTaskStatus(file.uri, 'skipped');
return { status: 'skipped' };
}
if (result.originalSuccess) {
await updateUploadTaskStatus(file.uri, 'success', result.fileIds?.original);
return { status: 'success' };
} else {
await updateUploadTaskStatus(file.uri, 'failed');
return { status: 'failed' };
}
} catch (e) {
console.error('Upload failed for', file.uri, e);
await updateUploadTaskStatus(file.uri, 'failed');
return { status: 'failed' };
}
})
);
// We don't wait for all uploads to complete. The function returns after starting them.
// The UI will then poll for progress.
Promise.allSettled(uploadPromises).then((results) => {
console.log('All upload tasks have been settled.');
const successfulUploads = results.filter(
(result) => result.status === 'fulfilled' && result.value.status === 'success'
).length;
console.log(`${successfulUploads} files uploaded successfully.`);
});
return startTime;
} catch (error) {
console.error('Error during upload trigger:', error);
return null;
}
}
// 定义后台任务
TaskManager.defineTask(BACKGROUND_UPLOAD_TASK, async () => {
try {
console.log('Running background upload task...');
const now = new Date();
const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
await triggerManualUpload(oneDayAgo, now);
return BackgroundTask.BackgroundTaskResult.Success;
} catch (error) {
console.error('Background task error:', error);
return BackgroundTask.BackgroundTaskResult.Failed;
}
});