6.2 KiB
6.2 KiB
Async Image Processing Implementation
Overview
The image processing system has been updated to handle external images asynchronously, preventing timeouts when processing stories with many images. This provides real-time progress updates to users showing which images are being processed.
Backend Components
1. ImageProcessingProgressService
- Tracks progress for individual story image processing sessions
- Thread-safe with
ConcurrentHashMapfor multi-user support - Provides progress information: total images, processed count, current image, status, errors
2. AsyncImageProcessingService
- Handles asynchronous image processing using Spring's
@Asyncannotation - Counts external images before processing
- Provides progress callbacks during processing
- Updates story content when processing completes
- Automatic cleanup of progress data after completion
3. Enhanced ImageService
- Added
processContentImagesWithProgress()method with callback support - Progress callbacks provide real-time updates during image download/processing
- Maintains compatibility with existing synchronous processing
4. Updated StoryController
POST /api/storiesandPUT /api/stories/{id}now trigger async image processingGET /api/stories/{id}/image-processing-progressendpoint for progress polling- Processing starts immediately after story save and returns control to user
Frontend Components
1. ImageProcessingProgressTracker (Utility Class)
const tracker = new ImageProcessingProgressTracker(storyId);
tracker.onProgress((progress) => {
console.log(`Processing ${progress.processedImages}/${progress.totalImages}`);
});
tracker.onComplete(() => console.log('Done!'));
tracker.start();
2. ImageProcessingProgressComponent (React Component)
<ImageProcessingProgressComponent
storyId={storyId}
autoStart={true}
onComplete={() => refreshStory()}
/>
User Experience
Before (Synchronous)
- User saves story with external images
- Request hangs for 30+ seconds processing images
- Browser may timeout
- No feedback about progress
- User doesn't know if it's working
After (Asynchronous)
- User saves story with external images
- Save completes immediately
- Progress indicator appears: "Processing 5 images. Currently image 2 of 5..."
- User can continue using the application
- Progress updates every second
- Story automatically refreshes when processing completes
API Endpoints
Progress Endpoint
GET /api/stories/{id}/image-processing-progress
Response when processing:
{
"isProcessing": true,
"totalImages": 5,
"processedImages": 2,
"currentImageUrl": "https://example.com/image.jpg",
"status": "Processing image 3 of 5",
"progressPercentage": 40.0,
"completed": false,
"error": ""
}
Response when completed:
{
"isProcessing": false,
"totalImages": 5,
"processedImages": 5,
"currentImageUrl": "",
"status": "Completed: 5 images processed",
"progressPercentage": 100.0,
"completed": true,
"error": ""
}
Response when no processing:
{
"isProcessing": false,
"message": "No active image processing"
}
Integration Examples
React Hook Usage
import { useImageProcessingProgress } from '../utils/imageProcessingProgress';
function StoryEditor({ storyId }) {
const { progress, isTracking, startTracking } = useImageProcessingProgress(storyId);
const handleSave = async () => {
await saveStory();
startTracking(); // Start monitoring progress
};
return (
<div>
{isTracking && progress && (
<div className="progress-indicator">
Processing {progress.processedImages}/{progress.totalImages} images...
</div>
)}
<button onClick={handleSave}>Save Story</button>
</div>
);
}
Manual Progress Tracking
// After saving a story with external images
const tracker = new ImageProcessingProgressTracker(storyId);
tracker.onProgress((progress) => {
updateProgressBar(progress.progressPercentage);
showStatus(progress.status);
if (progress.currentImageUrl) {
showCurrentImage(progress.currentImageUrl);
}
});
tracker.onComplete((finalProgress) => {
hideProgressBar();
showNotification('Image processing completed!');
refreshStoryContent(); // Reload story with processed images
});
tracker.onError((error) => {
hideProgressBar();
showError(`Image processing failed: ${error}`);
});
tracker.start();
Configuration
Polling Interval
Default: 1 second (1000ms)
const tracker = new ImageProcessingProgressTracker(storyId, 500); // Poll every 500ms
Timeout
Default: 5 minutes (300000ms)
const tracker = new ImageProcessingProgressTracker(storyId, 1000, 600000); // 10 minute timeout
Spring Async Configuration
The backend uses Spring's default async executor. For production, consider configuring a custom thread pool in your application properties:
spring:
task:
execution:
pool:
core-size: 4
max-size: 8
queue-capacity: 100
Error Handling
Backend Errors
- Network timeouts downloading images
- Invalid image formats
- Disk space issues
- All errors are logged and returned in progress status
Frontend Errors
- Network failures during progress polling
- Timeout if processing takes too long
- Graceful degradation - user can continue working
Benefits
- No More Timeouts: Large image processing operations won't timeout HTTP requests
- Better UX: Users get real-time feedback about processing progress
- Improved Performance: Users can continue using the app while images process
- Error Visibility: Clear error messages when image processing fails
- Scalability: Multiple users can process images simultaneously without blocking
Future Enhancements
- WebSocket Support: Replace polling with WebSocket for real-time push updates
- Batch Processing: Queue multiple stories for batch image processing
- Retry Logic: Automatic retry for failed image downloads
- Progress Persistence: Save progress to database for recovery after server restart
- Image Optimization: Automatic resize/compress images during processing