220 lines
6.2 KiB
Markdown
220 lines
6.2 KiB
Markdown
# 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 `ConcurrentHashMap` for multi-user support
|
|
- Provides progress information: total images, processed count, current image, status, errors
|
|
|
|
### 2. `AsyncImageProcessingService`
|
|
- Handles asynchronous image processing using Spring's `@Async` annotation
|
|
- 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/stories` and `PUT /api/stories/{id}` now trigger async image processing
|
|
- `GET /api/stories/{id}/image-processing-progress` endpoint for progress polling
|
|
- Processing starts immediately after story save and returns control to user
|
|
|
|
## Frontend Components
|
|
|
|
### 1. `ImageProcessingProgressTracker` (Utility Class)
|
|
```typescript
|
|
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)
|
|
```tsx
|
|
<ImageProcessingProgressComponent
|
|
storyId={storyId}
|
|
autoStart={true}
|
|
onComplete={() => refreshStory()}
|
|
/>
|
|
```
|
|
|
|
## User Experience
|
|
|
|
### Before (Synchronous)
|
|
1. User saves story with external images
|
|
2. Request hangs for 30+ seconds processing images
|
|
3. Browser may timeout
|
|
4. No feedback about progress
|
|
5. User doesn't know if it's working
|
|
|
|
### After (Asynchronous)
|
|
1. User saves story with external images
|
|
2. Save completes immediately
|
|
3. Progress indicator appears: "Processing 5 images. Currently image 2 of 5..."
|
|
4. User can continue using the application
|
|
5. Progress updates every second
|
|
6. Story automatically refreshes when processing completes
|
|
|
|
## API Endpoints
|
|
|
|
### Progress Endpoint
|
|
```
|
|
GET /api/stories/{id}/image-processing-progress
|
|
```
|
|
|
|
**Response when processing:**
|
|
```json
|
|
{
|
|
"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:**
|
|
```json
|
|
{
|
|
"isProcessing": false,
|
|
"totalImages": 5,
|
|
"processedImages": 5,
|
|
"currentImageUrl": "",
|
|
"status": "Completed: 5 images processed",
|
|
"progressPercentage": 100.0,
|
|
"completed": true,
|
|
"error": ""
|
|
}
|
|
```
|
|
|
|
**Response when no processing:**
|
|
```json
|
|
{
|
|
"isProcessing": false,
|
|
"message": "No active image processing"
|
|
}
|
|
```
|
|
|
|
## Integration Examples
|
|
|
|
### React Hook Usage
|
|
```tsx
|
|
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
|
|
```typescript
|
|
// 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)
|
|
```typescript
|
|
const tracker = new ImageProcessingProgressTracker(storyId, 500); // Poll every 500ms
|
|
```
|
|
|
|
### Timeout
|
|
Default: 5 minutes (300000ms)
|
|
```typescript
|
|
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:
|
|
|
|
```yaml
|
|
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
|
|
|
|
1. **No More Timeouts**: Large image processing operations won't timeout HTTP requests
|
|
2. **Better UX**: Users get real-time feedback about processing progress
|
|
3. **Improved Performance**: Users can continue using the app while images process
|
|
4. **Error Visibility**: Clear error messages when image processing fails
|
|
5. **Scalability**: Multiple users can process images simultaneously without blocking
|
|
|
|
## Future Enhancements
|
|
|
|
1. **WebSocket Support**: Replace polling with WebSocket for real-time push updates
|
|
2. **Batch Processing**: Queue multiple stories for batch image processing
|
|
3. **Retry Logic**: Automatic retry for failed image downloads
|
|
4. **Progress Persistence**: Save progress to database for recovery after server restart
|
|
5. **Image Optimization**: Automatic resize/compress images during processing |