15 KiB
15 KiB
Image-Uploader Erweiterung: Multi-Image Upload mit Beschreibung
🎯 Ziel
Erweiterung der bestehenden Single-Image-Upload-Funktionalität zu einem Multi-Image-Upload mit Beschreibungstext für spätere Slideshow-Nutzung.
📊 Aufwandsschätzung
Geschätzter Aufwand: 8-12 Stunden (verteilt auf 2-3 Arbeitstage)
Komplexitätsbewertung: ⭐⭐⭐☆☆ (Mittel)
🔄 Änderungsübersicht
Frontend Änderungen (5-7 Stunden)
- Neue Multi-Upload-Komponente
- UI für Beschreibungstext
- Vorschau-Galerie
- Upload-Progress für mehrere Dateien
Backend Änderungen (2-3 Stunden)
- Neue API-Endpoints
- Datenbank/JSON-Struktur für Upload-Gruppen
- Batch-Upload-Verarbeitung
Integration & Testing (1-2 Stunden)
- Frontend-Backend-Integration
- Error Handling
- UI/UX Tests
🏗️ Technische Umsetzung
1. Backend-Erweiterungen
1.1 Neue Datenstruktur
// Neue Upload-Group Struktur
{
groupId: "unique-group-id",
description: "Benutzer-Beschreibung",
uploadDate: "2025-10-11T10:30:00Z",
images: [
{
fileName: "abc123.jpg",
originalName: "foto1.jpg",
filePath: "/upload/abc123.jpg",
uploadOrder: 1
},
{
fileName: "def456.png",
originalName: "foto2.png",
filePath: "/upload/def456.png",
uploadOrder: 2
}
]
}
1.2 Neue API-Endpoints
POST /api/upload/batch- Multi-Image UploadGET /api/groups/:groupId- Upload-Gruppe abrufenGET /api/groups- Alle Upload-Gruppen auflistenGET /api/slideshow/:groupId- Slideshow-Daten
1.3 Dateien zu erstellen/ändern:
backend/src/
├── routes/
│ ├── upload.js # ✏️ Erweitern
│ ├── batch-upload.js # 🆕 Neu
│ └── groups.js # 🆕 Neu
├── models/
│ └── uploadGroup.js # 🆕 Neu
├── utils/
│ └── groupStorage.js # 🆕 Neu (JSON-basiert)
└── constants.js # ✏️ Neue Endpoints hinzufügen
2. Frontend-Erweiterungen
2.1 Neue Komponenten
frontend/src/Components/
├── ComponentUtils/
│ ├── MultiImageUpload.js # 🆕 Haupt-Upload-Komponente
│ ├── ImagePreviewGallery.js # 🆕 Vorschau der ausgewählten Bilder
│ ├── DescriptionInput.js # 🆕 Textfeld für Beschreibung
│ ├── UploadProgress.js # 🆕 Progress-Anzeige für alle Dateien
│ └── Css/
│ ├── MultiUpload.css # 🆕 Styling
│ └── ImageGallery.css # 🆕 Galerie-Styling
├── Pages/
│ ├── MultiUploadPage.js # 🆕 Neue Seite für Multi-Upload
│ ├── SlideshowPage.js # 🆕 Slideshow-Anzeige
│ └── GroupsOverviewPage.js # 🆕 Übersicht aller Upload-Gruppen
└── Utils/
└── batchUpload.js # 🆕 Batch-Upload-Logik
2.2 Routing-Erweiterungen
// Neue Routen in App.js
<Route path="/multi-upload" component={MultiUploadPage} />
<Route path="/slideshow/:groupId" component={SlideshowPage} />
<Route path="/groups" component={GroupsOverviewPage} />
📋 Detaillierte Implementierungsschritte
Phase 1: Backend-Grundlage (2-3h)
Schritt 1.1: Upload-Gruppen Datenmodell
// backend/src/models/uploadGroup.js
class UploadGroup {
constructor(description) {
this.groupId = generateId();
this.description = description;
this.uploadDate = new Date().toISOString();
this.images = [];
}
addImage(fileName, originalName, uploadOrder) {
this.images.push({
fileName,
originalName,
filePath: `/upload/${fileName}`,
uploadOrder
});
}
}
Schritt 1.2: JSON-basierte Speicherung
// backend/src/utils/groupStorage.js
const fs = require('fs');
const path = require('path');
const GROUPS_FILE = path.join(__dirname, '../data/upload-groups.json');
class GroupStorage {
static saveGroup(group) {
// JSON-Datei lesen, Gruppe hinzufügen, zurückschreiben
}
static getGroup(groupId) {
// Gruppe aus JSON-Datei laden
}
static getAllGroups() {
// Alle Gruppen laden
}
}
Schritt 1.3: Batch-Upload API
// backend/src/routes/batch-upload.js
router.post('/api/upload/batch', (req, res) => {
const { description } = req.body;
const files = req.files.images; // Array von Dateien
const group = new UploadGroup(description);
// Alle Dateien verarbeiten
files.forEach((file, index) => {
const fileName = generateId() + '.' + getFileExtension(file.name);
file.mv(`upload/${fileName}`);
group.addImage(fileName, file.name, index + 1);
});
GroupStorage.saveGroup(group);
res.json({ groupId: group.groupId, message: 'Upload successful' });
});
Phase 2: Frontend Multi-Upload UI (3-4h)
Schritt 2.1: Multi-Image Dropzone
// frontend/src/Components/ComponentUtils/MultiImageUpload.js
import { useDropzone } from 'react-dropzone';
function MultiImageUpload({ onImagesSelected }) {
const { getRootProps, getInputProps, acceptedFiles } = useDropzone({
accept: 'image/*',
multiple: true,
onDrop: (files) => {
onImagesSelected(files);
}
});
return (
<div {...getRootProps()} className="multi-dropzone">
<input {...getInputProps()} />
<p>Ziehe mehrere Bilder hierher oder klicke zum Auswählen</p>
<p>({acceptedFiles.length} Dateien ausgewählt)</p>
</div>
);
}
Schritt 2.2: Bild-Vorschau Galerie
// frontend/src/Components/ComponentUtils/ImagePreviewGallery.js
function ImagePreviewGallery({ images, onRemoveImage, onReorderImages }) {
return (
<div className="image-preview-gallery">
{images.map((image, index) => (
<div key={index} className="image-preview-item">
<img src={URL.createObjectURL(image)} alt={`Preview ${index + 1}`} />
<button onClick={() => onRemoveImage(index)}>✕</button>
<div className="image-order">{index + 1}</div>
</div>
))}
</div>
);
}
Schritt 2.3: Beschreibungs-Input
// frontend/src/Components/ComponentUtils/DescriptionInput.js
function DescriptionInput({ description, onDescriptionChange, maxLength = 500 }) {
return (
<div className="description-input">
<label>Beschreibung für diese Bildersammlung:</label>
<textarea
value={description}
onChange={(e) => onDescriptionChange(e.target.value)}
maxLength={maxLength}
placeholder="Beschreibe diese Bildersammlung für die spätere Slideshow..."
/>
<div className="character-count">{description.length}/{maxLength}</div>
</div>
);
}
Phase 3: Upload-Logik & Progress (2-3h)
Schritt 3.1: Batch-Upload Funktion
// frontend/src/Utils/batchUpload.js
async function uploadImageBatch(images, description, onProgress) {
const formData = new FormData();
images.forEach((image, index) => {
formData.append('images', image);
});
formData.append('description', description);
try {
const response = await fetch('/api/upload/batch', {
method: 'POST',
body: formData,
onUploadProgress: (progressEvent) => {
const progress = (progressEvent.loaded / progressEvent.total) * 100;
onProgress(progress);
}
});
return await response.json();
} catch (error) {
throw new Error(`Upload failed: ${error.message}`);
}
}
Schritt 3.2: Upload Progress Komponente
// frontend/src/Components/ComponentUtils/UploadProgress.js
function UploadProgress({ progress, currentFile, totalFiles }) {
return (
<div className="upload-progress">
<div className="progress-bar">
<div className="progress-fill" style={{ width: `${progress}%` }}></div>
</div>
<div className="progress-text">
{currentFile && `Uploading: ${currentFile} (${Math.round(progress)}%)`}
{totalFiles && `${currentFile} von ${totalFiles} Dateien`}
</div>
</div>
);
}
Phase 4: Hauptseite Integration (1-2h)
Schritt 4.1: Multi-Upload Seite
// frontend/src/Components/Pages/MultiUploadPage.js
function MultiUploadPage() {
const [selectedImages, setSelectedImages] = useState([]);
const [description, setDescription] = useState('');
const [uploading, setUploading] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
const handleUpload = async () => {
if (selectedImages.length === 0) return;
setUploading(true);
try {
const result = await uploadImageBatch(
selectedImages,
description,
setUploadProgress
);
// Redirect zur Slideshow oder Erfolgsseite
history.push(`/slideshow/${result.groupId}`);
} catch (error) {
// Error handling
} finally {
setUploading(false);
}
};
return (
<div className="multi-upload-page">
<Navbar />
<div className="upload-container">
<MultiImageUpload onImagesSelected={setSelectedImages} />
{selectedImages.length > 0 && (
<>
<ImagePreviewGallery
images={selectedImages}
onRemoveImage={removeImageAtIndex}
onReorderImages={reorderImages}
/>
<DescriptionInput
description={description}
onDescriptionChange={setDescription}
/>
<button
onClick={handleUpload}
disabled={uploading || selectedImages.length === 0}
className="upload-button"
>
{uploading ? 'Uploading...' : `${selectedImages.length} Bilder hochladen`}
</button>
</>
)}
{uploading && (
<UploadProgress
progress={uploadProgress}
totalFiles={selectedImages.length}
/>
)}
</div>
</div>
);
}
Phase 5: Slideshow & Navigation (2h)
Schritt 5.1: Slideshow Komponente
// frontend/src/Components/Pages/SlideshowPage.js
function SlideshowPage() {
const { groupId } = useParams();
const [group, setGroup] = useState(null);
const [currentImageIndex, setCurrentImageIndex] = useState(0);
useEffect(() => {
fetch(`/api/groups/${groupId}`)
.then(res => res.json())
.then(setGroup);
}, [groupId]);
if (!group) return <div>Loading...</div>;
return (
<div className="slideshow-page">
<div className="slideshow-header">
<h1>{group.description}</h1>
<p>Hochgeladen am: {new Date(group.uploadDate).toLocaleDateString()}</p>
</div>
<div className="slideshow-container">
<img
src={group.images[currentImageIndex].filePath}
alt={`Bild ${currentImageIndex + 1}`}
className="slideshow-image"
/>
<div className="slideshow-controls">
<button onClick={() => setCurrentImageIndex(prev =>
prev > 0 ? prev - 1 : group.images.length - 1
)}>
‹ Vorheriges
</button>
<span>{currentImageIndex + 1} / {group.images.length}</span>
<button onClick={() => setCurrentImageIndex(prev =>
prev < group.images.length - 1 ? prev + 1 : 0
)}>
Nächstes ›
</button>
</div>
</div>
<div className="slideshow-thumbnails">
{group.images.map((image, index) => (
<img
key={index}
src={image.filePath}
alt={`Thumbnail ${index + 1}`}
className={`thumbnail ${index === currentImageIndex ? 'active' : ''}`}
onClick={() => setCurrentImageIndex(index)}
/>
))}
</div>
</div>
);
}
🎨 UI/UX Verbesserungen
Drag & Drop Features
- Bilder-Reihenfolge ändern: Drag & Drop in der Vorschau
- Bilder entfernen: X-Button auf jedem Vorschaubild
- Bulk-Aktionen: Alle entfernen, Reihenfolge umkehren
Responsive Design
- Mobile-optimiert: Touch-friendly Upload und Slideshow
- Tablet-Ansicht: Optimierte Galerie-Darstellung
- Desktop: Erweiterte Features wie Keyboard-Navigation
Benutzerfreundlichkeit
- Progress-Feedback: Echtzeitanzeige des Upload-Fortschritts
- Error Handling: Klare Fehlermeldungen bei Upload-Problemen
- Auto-Save: Beschreibung zwischenspeichern
- Vorschau-Modus: Slideshow vor Upload testen
🧪 Testing-Strategie
Unit Tests
- Upload-Gruppe Datenmodell
- Batch-Upload API-Endpoints
- Frontend-Komponenten (Jest/React Testing Library)
Integration Tests
- End-to-End Upload-Flow
- Slideshow-Navigation
- Error-Szenarien
Performance Tests
- Multiple große Dateien (>10MB)
- Viele kleine Dateien (>50 Bilder)
- Speicher-Verbrauch bei großen Uploads
🚀 Deployment-Überlegungen
Datei-Größe Limits
// Backend-Konfiguration erweitern
app.use(fileUpload({
limits: {
fileSize: 50 * 1024 * 1024, // 50MB pro Datei
files: 20 // Max 20 Dateien pro Upload
},
}));
Speicher-Management
- Cleanup-Job: Alte Upload-Gruppen nach X Tagen löschen
- Komprimierung: Automatische Bildkomprimierung für große Dateien
- CDN-Integration: Für bessere Performance bei vielen Bildern
Sicherheit
- File-Type Validation: Nur erlaubte Bildformate
- Virus-Scanning: Optional für Produktionsumgebung
- Rate Limiting: Upload-Beschränkungen pro IP/User
📈 Erweiterungs-Möglichkeiten (Zukunft)
Erweiterte Features
- Benutzer-Accounts: Upload-Gruppen Benutzern zuordnen
- Tagging-System: Bilder mit Tags versehen
- Sharing: Upload-Gruppen per Link teilen
- Export: Slideshow als Video oder PDF exportieren
Slideshow-Features
- Autoplay: Automatischer Bildwechsel
- Übergangs-Effekte: Fade, Slide, etc.
- Hintergrundmusik: Audio-Upload für Slideshows
- Vollbild-Modus: Immersive Slideshow-Erfahrung
Admin-Features
- Upload-Statistiken: Dashboard mit Nutzungsmetriken
- Content-Moderation: Gemeldete Inhalte prüfen
- Bulk-Operations: Mehrere Gruppen gleichzeitig verwalten
⚡ Quick-Start Implementierung
Für einen schnellen Proof-of-Concept (2-3 Stunden):
- Backend: Erweitere
/uploadRoute für Array-Handling - Frontend: Ändere bestehende Dropzone auf
multiple: true - Einfache Galerie: Zeige alle Bilder einer "Session" an
- Basis-Slideshow: Einfache Vor/Zurück-Navigation
Dies würde eine funktionale Basis schaffen, die später ausgebaut werden kann.
🎯 Erfolgskriterien
Must-Have
- ✅ Mehrere Bilder gleichzeitig auswählen
- ✅ Beschreibungstext hinzufügen
- ✅ Upload als zusammengehörige Gruppe
- ✅ Einfache Slideshow-Anzeige
- ✅ Mobile-Kompatibilität
Nice-to-Have
- 🎨 Drag & Drop Reihenfolge ändern
- 📊 Upload-Progress mit Details
- 🖼️ Thumbnail-Navigation in Slideshow
- 💾 Auto-Save der Beschreibung
- 🔄 Batch-Operations (alle entfernen, etc.)
Future Features
- 👤 User-Management
- 🏷️ Tagging-System
- 📤 Export-Funktionen
- 🎵 Audio-Integration
Fazit: Die Erweiterung ist gut machbar und baut logisch auf der bestehenden Architektur auf. Der modulare Ansatz ermöglicht schrittweise Implementierung und spätere Erweiterungen.