Datei-Uploads mit Bootstrap 5 implementieren
Datei-Uploads sind in modernen Webanwendungen unverzichtbar. Bootstrap 5 stellt Werkzeuge bereit, mit denen Sie elegante und benutzerfreundliche Oberflächen für Datei-Uploads gestalten. Dieser Leitfaden führt Sie durch die Implementierung von Datei-Uploads mit Bootstrap 5, einschließlich Drag-and-Drop-Funktionalität, eigener Upload-Schaltflächen und einer Fortschrittsanzeige per AJAX.
Einführung in Datei-Uploads mit Bootstrap 5
Bootstrap 5 bietet zahlreiche Komponenten für den Aufbau responsiver Weboberflächen. Eine eigene Komponente für Datei-Uploads gibt es zwar nicht, doch mit den Formularsteuerelementen von Bootstrap erstellen Sie individuelle Upload-Formulare, die sich nahtlos in Ihre Anwendung einfügen.
In diesem Tutorial behandeln wir:
- Einrichten einer Bootstrap-5-Umgebung
- Erstellen eines einfachen Datei-Upload-Formulars
- Erweitern um Drag-and-Drop-Funktionalität
- Hochladen per AJAX mit Fortschrittsanzeige
- Verarbeiten von Datei-Uploads auf der Serverseite
- Umsetzen bewährter Sicherheitsverfahren und einer Fehlerbehandlung
Einrichten einer Bootstrap-5-Umgebung
Richten Sie zunächst eine einfache Bootstrap-5-Umgebung ein. Sie können Bootstrap über ein CDN einbinden oder lokal installieren.
Einbindung über ein CDN:
Fügen Sie die folgenden Links in den Abschnitt <head> Ihrer HTML-Datei ein:
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
crossorigin="anonymous"
/>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"
></script>
Einbindung über npm:
Wenn Sie ein Build-Werkzeug verwenden, installieren Sie Bootstrap über npm:
npm install bootstrap@5.3.3
Erstellen eines einfachen Datei-Upload-Formulars
Erstellen wir mit den Formularsteuerelementen von Bootstrap ein einfaches Datei-Upload-Formular:
<form action="/upload" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label for="formFile" class="form-label">Choose a file to upload</label>
<input
class="form-control"
type="file"
id="formFile"
name="file"
required
accept="image/jpeg,image/png,application/pdf"
/>
</div>
<div class="progress d-none mb-3">
<div class="progress-bar" role="progressbar" style="width: 0%"></div>
</div>
<button type="submit" class="btn btn-primary">Upload</button>
</form>
Erweitern um Drag-and-Drop-Funktionalität
Ersetzen Sie im obigen Formular nur den ersten Abschnitt zur Dateiauswahl vom Typ .mb-3 durch diese Gruppe.
Behalten Sie das umgebende Formular, den Fortschrittsbalken und die Absenden-Schaltfläche für den
AJAX-Handler weiter unten in diesem Leitfaden bei. Platzieren Sie das Skript nach dem Formular:
<div class="mb-3">
<label for="formFile" class="form-label">Upload your file</label>
<button type="button" id="dropZone" class="w-100 rounded border border-primary p-4 text-center">
Drag and drop a file here or click to select
</button>
<div id="errorMessage" role="alert" class="alert alert-danger d-none mt-2"></div>
<input
type="file"
id="formFile"
name="file"
class="d-none"
accept="image/jpeg,image/png,application/pdf"
/>
</div>
<script>
const dropZone = document.getElementById('dropZone')
const formFile = document.getElementById('formFile')
const errorMessage = document.getElementById('errorMessage')
const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf']
const maxSize = 5 * 1024 * 1024 // 5MB
function validateFile(file) {
if (!file) {
throw new Error('Please select one file to upload.')
}
if (!allowedTypes.includes(file.type)) {
throw new Error('Invalid file type. Please upload JPEG, PNG or PDF files.')
}
if (file.size > maxSize) {
throw new Error('File too large. Maximum size is 5MB.')
}
}
function showError(message) {
errorMessage.textContent = message
errorMessage.classList.remove('d-none')
}
function hideError() {
errorMessage.classList.add('d-none')
}
dropZone.addEventListener('click', () => formFile.click())
dropZone.addEventListener('dragover', (e) => {
e.preventDefault()
dropZone.classList.add('bg-light')
})
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('bg-light')
})
dropZone.addEventListener('drop', (e) => {
e.preventDefault()
dropZone.classList.remove('bg-light')
hideError()
try {
const droppedFiles = e.dataTransfer?.files
if (!droppedFiles || droppedFiles.length !== 1) {
throw new Error('Please drop exactly one file.')
}
const file = droppedFiles[0]
validateFile(file)
formFile.files = droppedFiles
updateDropZoneText()
} catch (error) {
formFile.value = ''
updateDropZoneText()
showError(error.message)
}
})
formFile.addEventListener('change', () => {
hideError()
try {
if (formFile.files.length > 0) {
validateFile(formFile.files[0])
updateDropZoneText()
}
} catch (error) {
showError(error.message)
formFile.value = ''
updateDropZoneText()
}
})
function updateDropZoneText() {
dropZone.textContent =
formFile.files.length > 0
? formFile.files[0].name
: 'Drag and drop a file here or click to select'
}
</script>
Verarbeiten von Datei-Uploads auf der Serverseite
Installieren Sie Express und Multer und verwenden Sie dabei die korrigierte Multer-Version, die im zugehörigen Sicherheitshinweis dokumentiert ist:
npm install express@5 multer@2.3.0
Dieser Server verarbeitet /upload. Liefern Sie HTML und Skripte in Ihrer Anwendung vom selben
Origin aus und halten Sie uploads/ außerhalb jedes Verzeichnisses, das von einer Middleware für
statische Dateien ausgeliefert wird. Der folgende MIME-Filter prüft ausschließlich die vom Client
gelieferten Metadaten. Prüfen Sie die Dateiinhalte und wenden Sie eine Autorisierung an, bevor Sie
gespeicherte Dateien für Nutzer verfügbar machen.
const express = require('express')
const multer = require('multer')
const path = require('path')
const app = express()
const storage = multer.diskStorage({
destination: 'uploads/',
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9)
cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname))
},
})
const fileFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf']
if (!allowedTypes.includes(file.mimetype)) {
cb(new Error('Invalid file type'), false)
return
}
cb(null, true)
}
const upload = multer({
storage: storage,
fileFilter: fileFilter,
limits: {
fileSize: 5 * 1024 * 1024, // 5MB
files: 1,
fields: 0,
parts: 2,
},
})
app.post('/upload', upload.single('file'), (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'No file uploaded.' })
}
res.json({ message: 'File uploaded successfully' })
})
app.use((error, req, res, next) => {
if (error instanceof multer.MulterError) {
const status = error.code === 'LIMIT_FILE_SIZE' ? 413 : 400
return res.status(status).json({ error: 'File exceeds an upload limit.' })
}
if (error.message === 'Invalid file type') {
return res.status(400).json({ error: 'Unsupported file type.' })
}
console.error('Upload failed', { code: error.code ?? 'UNKNOWN' })
res.status(500).json({ error: 'Unable to upload the file.' })
})
app.listen(3000, () => {
console.log('Server started on http://localhost:3000')
})
Hochladen per AJAX mit Fortschrittsanzeige
Für eine bessere Nutzererfahrung können Sie Dateien asynchron hochladen und den Upload-Fortschritt in Echtzeit zurückmelden. Das folgende Beispiel zeigt, wie Sie das Absenden eines Formulars abfangen, die Datei per XMLHttpRequest senden und einen Bootstrap-Fortschrittsbalken in Echtzeit aktualisieren:
Dieser Handler baut auf der Drag-and-Drop-Variante auf. Führen Sie ihn nach jenem Skript aus, damit validateFile() verfügbar ist.
Das Erreichen von 100 % bedeutet, dass der Request-Body gesendet wurde; für den Erfolg ist zusätzlich eine erfolgreiche Serverantwort nötig.
const form = document.querySelector('form')
const progressBarEl = document.querySelector('.progress')
const progressFill = document.querySelector('.progress-bar')
form.addEventListener('submit', (e) => {
e.preventDefault()
try {
validateFile(form.querySelector('input[type="file"]').files[0])
} catch (error) {
alert(error.message)
return
}
const formData = new FormData(form)
const xhr = new XMLHttpRequest()
xhr.open('POST', form.getAttribute('action'), true)
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable && event.total > 0) {
const percentage = (event.loaded / event.total) * 100
progressFill.style.width = percentage + '%'
progressFill.textContent = Math.round(percentage) + '%'
progressBarEl.classList.remove('d-none')
}
})
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
alert('File uploaded successfully')
} else {
alert('File upload failed')
}
progressBarEl.classList.add('d-none')
progressFill.style.width = '0%'
progressFill.textContent = ''
}
xhr.onerror = () => {
alert('Upload error')
progressBarEl.classList.add('d-none')
progressFill.style.width = '0%'
progressFill.textContent = ''
}
xhr.send(formData)
})
Sicherheitsaspekte
Berücksichtigen Sie bei der Implementierung von Datei-Uploads diese Sicherheitsmaßnahmen:
- Validieren Sie Dateitypen sowohl clientseitig als auch serverseitig
- Setzen Sie Beschränkungen für die Dateigröße um
- Nutzen Sie sichere Speicherorte für Dateien
- Setzen Sie eine Ratenbegrenzung für Uploads um, um Missbrauch zu verhindern
- Prüfen Sie hochgeladene Dateien auf Schadsoftware
- Setzen Sie CSRF-Schutz ein
- Sorgen Sie für eine saubere Fehlerbehandlung, einschließlich Fehler-Logging für das Sicherheitsmonitoring
Fazit
Indem Sie Bootstrap 5 mit robusten Techniken für Datei-Uploads und geeigneten Sicherheitsmaßnahmen kombinieren, schaffen Sie ein nahtloses und benutzerfreundliches System für Datei-Uploads. Die Implementierung umfasst Drag-and-Drop-Funktionalität, eine AJAX-basierte Fortschrittsanzeige und eine umfassende Fehlerbehandlung.
Für eine produktionsreife Lösung, die Datei-Uploads und deren Verarbeitung in großem Maßstab bewältigt, sollten Sie Transloadit in Betracht ziehen. Transloadit bietet zuverlässige Dienste für das Hochladen und Encoding von Dateien, die Ihren Entwicklungsprozess verschlanken und komplexe Aufgaben der Dateiverarbeitung übernehmen.
