Individuelle Datei-Upload-Oberfläche mit JavaScript erstellen
Standard-Dateieingabefelder in HTML bieten grundlegende Funktionen, sind aber oft nicht flexibel und benutzerfreundlich genug für moderne Webanwendungen. In diesem DevTip erstellen wir mit JavaScript eine individuelle Oberfläche für Datei-Uploads, die sowohl das Erscheinungsbild als auch die Funktionalität des Standard-Browser-Eingabefelds verbessert. Diese Lösung sorgt für ein ansprechendes Nutzungserlebnis durch Uploads mehrerer Dateien, Dateivorschauen, eine robuste Validierung einschließlich Prüfungen der Dateisignatur sowie Fortschrittsanzeigen während der Uploads.
Einführung in individuelle Oberflächen für Datei-Uploads
Mit einer individuellen Oberfläche für Datei-Uploads überwinden Sie die Grenzen des Standard-Dateieingabefelds. Indem Sie einen Dateiuploader in das Design Ihrer Anwendung integrieren, können Sie Uploads mehrerer Dateien effizient abwickeln und sofortiges visuelles Feedback mit Dateivorschauen und passgenauen Validierungsmeldungen bieten.
Einschränkungen von Standard-Dateieingabefeldern
Das Standardelement <input type="file"> bringt mehrere Herausforderungen mit sich:
- Uneinheitliche Darstellung in verschiedenen Browsern.
- Begrenzte Kontrolle über das Erscheinungsbild.
- Fehlende Unterstützung für erweiterte Funktionen wie die Auswahl mehrerer Dateien, Vorschauen oder eigene Validierungsmeldungen.
Die HTML-Struktur aufsetzen
Erstellen Sie zunächst eine einfache HTML-Struktur für unseren individuellen Dateiuploader:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="csrf-token" content="your-csrf-token-here" />
<title>Custom File Upload UI</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="file-upload" id="drop-zone">
<input type="file" id="file-input" multiple accept="image/jpeg,image/png,image/gif" />
<label for="file-input">Choose Files or Drag & Drop</label>
<div id="file-preview"></div>
<div id="error-messages"></div>
<div id="upload-progress"></div>
</div>
<button id="upload-button">Upload Files</button>
<script src="script.js"></script>
</body>
</html>
Diese Struktur umfasst:
- Ein Element
<input>mit Einschränkungen für den Dateityp. - Eine Drag-and-drop-Zone für Datei-Uploads.
- Container für Dateivorschauen, Fehlermeldungen und den Upload-Fortschritt.
- Ein Meta-Tag für das CSRF-Token zur Erhöhung der Sicherheit.
Das Dateieingabefeld mit CSS gestalten
Wenden Sie Stile auf unsere individuelle Komponente für Datei-Uploads an:
.file-upload {
position: relative;
display: inline-block;
padding: 20px;
border: 2px dashed #ccc;
border-radius: 8px;
transition: border-color 0.3s;
}
.file-upload.dragover {
border-color: #007bff;
background-color: rgba(0, 123, 255, 0.1);
}
#file-input {
position: absolute;
width: 1px;
height: 1px;
clip-path: inset(50%);
overflow: hidden;
}
#file-input:focus-visible + label {
outline: 2px solid currentColor;
outline-offset: 3px;
}
.file-upload label {
display: inline-block;
padding: 10px 20px;
background-color: #007bff;
color: #fff;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s;
}
.file-upload label:hover {
background-color: #0056b3;
}
#file-preview {
margin-top: 20px;
display: flex;
flex-wrap: wrap;
gap: 10px;
}
#file-preview img {
width: 100px;
height: 100px;
object-fit: cover;
border-radius: 4px;
}
#error-messages {
color: #dc3545;
margin-top: 10px;
}
#upload-progress {
margin-top: 10px;
height: 4px;
background-color: #e9ecef;
border-radius: 4px;
overflow: hidden;
}
#upload-progress .progress-bar {
height: 100%;
background-color: #28a745;
transition: width 0.3s ease;
}
#upload-button {
margin-top: 20px;
padding: 10px 20px;
background-color: #28a745;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
}
#upload-button:hover {
background-color: #218838;
}
JavaScript für erweiterte Funktionalität implementieren
Ergänzen Sie JavaScript, um die Dateiauswahl, das Rendern der Vorschauen, die Validierung und die Verfolgung des Upload-Fortschritts zu steuern.
const fileInput = document.getElementById('file-input')
const preview = document.getElementById('file-preview')
const uploadButton = document.getElementById('upload-button')
const dropZone = document.getElementById('drop-zone')
const errorMessages = document.getElementById('error-messages')
const uploadProgress = document.getElementById('upload-progress')
let selectedFiles = []
// Enable drag and drop functionality with visual feedback
dropZone.addEventListener('dragover', (e) => {
e.preventDefault()
dropZone.classList.add('dragover')
})
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('dragover')
})
dropZone.addEventListener('drop', async (e) => {
e.preventDefault()
dropZone.classList.remove('dragover')
await handleFiles(Array.from(e.dataTransfer.files))
})
fileInput.addEventListener('change', async (e) => {
await handleFiles(Array.from(e.target.files))
})
uploadButton.addEventListener('click', handleUpload)
async function handleFiles(files) {
preview.innerHTML = ''
errorMessages.innerHTML = ''
selectedFiles = []
for (const file of files) {
const isValid = await validateFile(file)
if (isValid) {
selectedFiles.push(file)
const reader = new FileReader()
reader.onload = (e) => createPreview(e.target.result, file.name)
reader.readAsDataURL(file)
}
}
}
async function validateFile(file) {
const validTypes = ['image/jpeg', 'image/png', 'image/gif']
const maxSize = 2 * 1024 * 1024 // 2 MB
if (!validTypes.includes(file.type)) {
showError(`${file.name} is not a supported file type`)
return false
}
if (file.size > maxSize) {
showError(`${file.name} exceeds the 2 MB size limit`)
return false
}
const isSignatureValid = await checkFileSignature(file)
if (!isSignatureValid) {
showError(`${file.name} failed file signature validation`)
}
return isSignatureValid
}
function checkFileSignature(file) {
return new Promise((resolve) => {
const reader = new FileReader()
reader.onerror = () => resolve(false)
reader.onload = (e) => {
const arr = new Uint8Array(e.target.result).subarray(0, 4)
if (arr.length < 4) return resolve(false)
let header = ''
for (let i = 0; i < arr.length; i++) {
header += arr[i].toString(16).padStart(2, '0')
}
const validSignatures = {
'89504e47': 'image/png',
'47494638': 'image/gif',
}
const detectedType = header.startsWith('ffd8ff') ? 'image/jpeg' : validSignatures[header]
resolve(detectedType === file.type)
}
reader.readAsArrayBuffer(file.slice(0, 4))
})
}
function createPreview(src, alt) {
const container = document.createElement('div')
container.className = 'preview-item'
const img = document.createElement('img')
img.src = src
img.alt = alt
const fileName = document.createElement('span')
fileName.textContent = alt
container.appendChild(img)
container.appendChild(fileName)
preview.appendChild(container)
}
function showError(message) {
const error = document.createElement('div')
error.textContent = message
errorMessages.appendChild(error)
}
function handleUpload() {
if (selectedFiles.length === 0) {
showError('Please select files to upload')
return
}
const formData = new FormData()
const csrfToken = document.querySelector('meta[name="csrf-token"]').content
selectedFiles.forEach((file) => {
formData.append('files[]', file)
})
formData.append('csrf_token', csrfToken)
const xhr = new XMLHttpRequest()
// Initialize progress tracking
uploadProgress.innerHTML = '<div class="progress-bar"></div>'
const progressBar = uploadProgress.querySelector('.progress-bar')
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percentComplete = (e.loaded / e.total) * 100
progressBar.style.width = percentComplete + '%'
}
})
xhr.onload = function () {
if (xhr.status === 200) {
preview.innerHTML = ''
uploadProgress.innerHTML = ''
showSuccess('Files uploaded successfully!')
} else {
showError('Upload failed. Please try again.')
}
}
xhr.onerror = function () {
showError('An error occurred during upload')
}
xhr.open('POST', '/upload', true)
xhr.send(formData)
}
function showSuccess(message) {
const success = document.createElement('div')
success.style.color = '#28a745'
success.textContent = message
errorMessages.appendChild(success)
}
Diese aktualisierte Implementierung enthält Unterstützung für Drag-and-drop, eine robuste asynchrone Dateivalidierung einschließlich Prüfungen der Dateisignatur sowie eine visuelle Fortschrittsanzeige während der Uploads. Außerdem stellt sie Feedback durch Fehler- und Erfolgsmeldungen sicher, abgesichert durch CSRF-Schutz.
Diese kurzen Signaturprüfungen verbessern das Feedback, können aber nicht belegen, dass eine Datei sicher oder auch nur vollständig decodierbar ist. Validieren Sie den Inhalt und das CSRF-Token auf dem Server, bevor Sie einem Upload vertrauen.
Browser-Kompatibilität
Verwenden Sie einen modernen Browser mit FileReader, FormData, Drag and Drop, XMLHttpRequest und
Unterstützung für async/await. Erkennen Sie die APIs per Feature-Detection und behalten Sie bei Bedarf
einen nativen Fallback auf das Dateieingabefeld bei. Polyfills allein können neuere
JavaScript-Syntax in älteren Browsern nicht zum Laufen bringen.
Fazit
Diese individuelle Oberfläche für Datei-Uploads zeigt einen modernen Ansatz für die Abwicklung von Datei-Uploads mit mehr Sicherheit und besserem Nutzungserlebnis. Durch die Integration von Funktionen wie Drag-and-drop, Fortschrittsanzeige und umfassender Validierung können Sie die Funktionalität des Standard-Dateieingabefelds deutlich verbessern.
Wenn Sie eine Lösung auf Enterprise-Niveau mit zusätzlichen Funktionen wie fortsetzbaren Uploads und Cloud-Storage-Integration suchen, sollten Sie Uppy 4.0 in Betracht ziehen. Uppy bietet TypeScript-Unterstützung und nahtlose Integrationen mit verbreiteten Frameworks wie React, Vue, Svelte, Angular und Next.js.
