Der File-Uploader 'Plupload' (https://www.plupload.com/) ermöglicht auf komfortable Weise, aus einem Browserfenster heraus Dateien auszuwählen, die dann in eine Cloud oder auf einen eigenen Webspace hochgeladen werden. Für jede einzelne Datei kann ein Fortschrittsbalken angezeigt werden.
In den gängigen Beispielen werden nach Auswahl der hochzuladenden Dateien die Dateinamen aller ausgewählten Dateien in einer Liste angezeigt. Oftmals handelt es sich um den Upload von Bildern, und es besteht der Wunsch, zusätzlich zu den Dateinamen ein Vorschaubild für jede ausgewählte Bilddatei anzuzeigen. So hätte man nochmals eine gute visuelle Kontrolle, ob man die richtigen Bilder ausgewählt hat, bevor man den Upload startet.
Hier soll eine einfache Lösung vorgestellt werden, wie man durch eine kleine Änderung des Javascript-Codes zusätzlich zu jedem Dateinamen der Auswahlliste ein Vorschaubild anzeigen kann.
Das komplette und voll funktionsfähige Beispiel kann hier heruntergeladen werden:
▸Download 'uploader.zip' (Letzte Änderung: 23.09.2023)
Anleitung:
Nun kann man im Internetbrowser die Webseite "https://MeineDomain.com/Uploader" aufrufen und Dateien hochladen.
Beim Einsatz im Internet sind auf jeden Fall noch zusätzliche Sicherheitsmechanismen zu integrieren, z. B. ein Login-System.
Die Webseite sieht nach dem Aufruf "https://MeineDomain.com/Uploader" zunächst so aus:
Nach dem Klick auf "BROWSE..." und Auswahl von Bildern sieht man die Liste mit den Vorschaubildern:
Nach dem Klick auf den Button "START" werden die Bilder mit Fortschrittsanzeige hochgeladen:
Für Dateien, die keine Bilder sind, werden Symbole angezeigt:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" dir="ltr"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="robots" content="noindex" /> <title>Uploader</title> <link rel="stylesheet" type="text/css" href="css/index.css"> <link rel="stylesheet" type="text/css" href="css/mobile.css"> <script type="text/javascript" src="js/plupload.full.min-2.3.9.js"></script> <script type="text/javascript" src="js/jquery-1.11.1.js" charset="UTF-8"></script> </head> <body> <header> <div id="title"> <h1>Uploader</h1> </div> </header> <div id="container"> <div id="hint"> <div id="hinttext"> <h3>Choose files</h3> <h4>Choose files and click START</h4> </div> </div> <div id="filelist"> <table></table> </div> <div id="buttons"> <button id="btnBrowse">BROWSE...</button> <button id="btnUpload" disabled="disabled">START</button> <button id="btnClear" disabled="disabled">CLEAR LIST</button> </div> <div id="console"></div> </div> <!-- "container" --> <script type="text/javascript" src="js/uploader.js"></script> </body> </html>
Der Aufruf dieser HTML-Datei müsste bei einer Verwendung im Internet noch durch ein Login-System geschützt werden.
var receiver = 'receiver.php'; var maxNumFiles = 100; var maxFileSize = '30 MB'; var console = $('#console'); var tableHeader = '<tr><th>File</th><th>Size</th><th>Del.</th><th>Upload</th></tr>'; var fileCountOld = 0; var fileIndex = 0; var maxLength; var innWidth; var filter = { max_file_size : maxFileSize, mime_types: [ {title : "Image files", extensions : "jpg,jpeg,gif,png,bmp,tif,tiff,svg"}, {title : "Pdf files", extensions : "pdf"}, {title : "Office files", extensions : "doc,docx,xlsx,pptx,pub,odt"}, {title : "Text files", extensions : "txt"}, {title : "Media files", extensions : "wav,mp3,mp4,ogg"}, {title : "Zip files", extensions : "zip,rar"} ] }; var uploader = new plupload.Uploader({ browse_button: 'btnBrowse', url: receiver, max_files: maxNumFiles, filters : filter }); uploader.init(); uploader.bind('FilesAdded', function(up, files) { var row; var re = /(?:\.([^.]+))?$/; if (up.files.length <= up.settings.max_files) { fileIndex = 0; plupload.each(files, function(file) { var ext = re.exec(file.name)[1]; row = '<tr id="' + file.id + '"><td class="first_col"><img id="im' + file.id + '" src="" width="auto" height="60" /><br />' + file.name.substr(0, maxLength) + '</td><td>' + plupload.formatSize(file.size) + '</td><td><a href="javascript:;" id="a' + file.id + '" class="delete"><img src="pics/Delete.png" class="deletePic" /></a>'+ '</td><td><div class="progressBorder"><div class="progress"></div></div><span class="percent"></span>'+ '</td></tr>'; $('#filelist table').append(row); if (ext.toLowerCase() == "jpg" || ext.toLowerCase() == "jpeg" || ext.toLowerCase() == "png" || ext.toLowerCase() == "gif" || ext.toLowerCase() == "bmp") { const reader = new FileReader(); reader.addEventListener('load', () => { document.getElementById('im' + file.id).src = reader.result; }); reader.readAsDataURL(files[fileIndex].getSource().getSource()); } else if (ext.toLowerCase() == "mp4") { document.getElementById('im' + file.id).src = "pics/VideoSymbol.png"; } else if (ext.toLowerCase() == "mp3" || ext.toLowerCase() == "wav") { document.getElementById('im' + file.id).src = "pics/MusicSymbol.png"; } else if (ext.toLowerCase() == "zip") { document.getElementById('im' + file.id).src = "pics/ZIP-Symbol.png"; } else if (ext.toLowerCase() == "pdf") { document.getElementById('im' + file.id).src = "pics/PDF-Symbol.png"; } else if (ext.toLowerCase() == "xlsx" || ext.toLowerCase() == "xlsm") { document.getElementById('im' + file.id).src = "pics/Excel-Symbol.png"; } else if (ext.toLowerCase() == "docx" || ext.toLowerCase() == "doc") { document.getElementById('im' + file.id).src = "pics/Word-Symbol.png"; } else if (ext.toLowerCase() == "pub") { document.getElementById('im' + file.id).src = "pics/Publisher-Symbol.png"; } else if (ext.toLowerCase() == "txt") { document.getElementById('im' + file.id).src = "pics/TXT-Symbol.png"; } else { document.getElementById('im' + file.id).src = "pics/FileSymbol.png"; } fileIndex = fileIndex + 1; }); plupload.each(files, function(file) { $('#' + file.id + ' .progressBorder').hide(); document.getElementById('a' + file.id).onclick = function() { if (up.state == plupload.STARTED && file.status == plupload.UPLOADING) { up.stop(); up.removeFile(file); up.start(); } else { up.removeFile(file); } $('#' + file.id).hide(500); if (up.files.length == 0) { $('#btnClear').prop('disabled', true); } }; }); fileCountOld = up.files.length; } else { var str; var anzahl = up.settings.max_files - fileCountOld; if (anzahl > 1) { str = "Es können höchstens noch " + anzahl + " Dateien hinzugefügt werden.<br />" } else if (anzahl == 1) { str = "Es kann höchstens noch 1 Datei hinzugefügt werden.<br />" } else { str = "Es kann keine Datei mehr hinzugefügt werden.<br />" } console.append("Die Liste kann höchstens " + up.settings.max_files + " Dateien enthalten!<br />" + str); up.splice(fileCountOld); } $('#btnClear').prop('disabled', false); }); uploader.bind('QueueChanged', function (up) { if (uploader.files.length == 0) { $('#btnUpload').prop('disabled', true); } else { $('#btnUpload').prop('disabled', false); $('#btnUpload').focus(); } if (up.files.length < fileCountOld) { fileCountOld = up.files.length; } }); uploader.bind('BeforeUpload', function(up, file) { $('#btnUpload').prop('disabled', true); $('#' + file.id + ' .progressBorder').show(); }); uploader.bind('UploadProgress', function(up, file) { $('#' + file.id + ' .percent').html(file.percent + '%'); $('#' + file.id + ' .progress').css('width', file.percent + '%'); }); uploader.bind('FileUploaded', function(up, file, res) { $('#' + file.id + ' .progressBorder').hide(300); setTimeout(setOkHook, 400, file.id); }); function setOkHook(id) { $('#' + id + ' .percent').append(" <img src='pics/Ok.png' class='picOk' />"); $('#a' + id).hide(100); } uploader.bind('UploadComplete', function (up, file) { $('#btnUpload').prop('disabled', true); $('#kommentarinput').val(""); $('#kommentaranzeige').css('display', 'none'); }); uploader.bind('Error', function(up, err) { var filesize = strFileSize(err.file.size); var meldung = "<span class='filename'>" + err.file.name + "</span><br />"; switch (err.code) { case -600: if (err.file.size > 0) { meldung += "Datei zu groß: " + filesize + " (max. " + filter.max_file_size + ")<br />"; } else { meldung += "Dateigröße ist 0 Bytes!<br />"; } break; case -601: meldung += "Unerlaubte Dateiendung!<br />"; break; default: meldung += "Error" + err.code + ": " + err.message + " (Filesize: " + filesize + ")<br />"; } meldung += "<br />"; $('#console').append(meldung); }); $('#btnBrowse').click(() => { console.html(""); }); $('#btnUpload').click(() => { uploader.start(); console.html(""); }); $('#btnClear').click(() => { $('#filelist table').html(tableHeader); uploader.splice(0, uploader.files.length); $('#btnClear').prop('disabled', true); console.html(""); fileCountOld = 0; return false; }); function strFileSize(bytes, decimalPlaces = 1, separator = ' ') { if (bytes) { const sizes = ['Bytes', 'kB', 'MB', 'GB', 'TB']; const i = Math.min(parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString(), 10), sizes.length - 1); return `${(bytes / (1024 ** i)).toFixed(i ? decimalPlaces : 0)}${separator}${sizes[i]}`; } return '0 Bytes'; } $(window).resize(function() { setMaxLength(); }); function setMaxLength() { innWidth = $(window).width(); if (innWidth <= 480) { maxLength = 32; } else { maxLength = 70; } } setMaxLength(); $('#btnUpload').prop('disabled', true); // for page refresh (F5) $('#btnClear').prop('disabled', true); // " $('#btnBrowse').focus(); $('#filelist table').html(tableHeader);
Um das Vorschaubild sichtbar zu machen, sind zwei Stellen in der Methode uploader.bind('FilesAdded', ...) entscheidend.
In der Zeile, die mit "row = ..." beginnt, werden die einzelnen Zeilen für die Anzeige der Upload-Liste zusammengestellt:
row = '<tr id="' + file.id +
'"><td class="first_col"><img id="im' + file.id + '" src="" width="auto" height="60" /><br />' + file.name.substr(0,
. . .
$('#filelist table').append(row);
Dabei wird in jeder Zeile ein img-Tag generiert, dessen ID sich aus den Buchstaben "im" und der vom Pluploader intern erzeugten File-ID zusammensetzt. Über diese ID kann dann dieses DOM-Element angesprochen werden und das zunächst leere src-Attribut gefüllt werden.
Dies geschieht innerhalb der unmittelbar folgenden IF-Abfrage:
if (ext.toLowerCase() == "jpg"
|| ext.toLowerCase() == "jpeg"
|| ext.toLowerCase() == "png"
|| ext.toLowerCase() == "gif"
|| ext.toLowerCase() == "bmp") {
const reader = new FileReader();
reader.addEventListener('load', () => {
document.getElementById('im' + file.id).src = reader.result;
});
reader.readAsDataURL(files[fileIndex].getSource().getSource());
} else if (ext.toLowerCase() == "mp4") {
Dort wird geprüft, ob es sich bei der Datei um ein Bild handelt. Wenn ja, wird mit der Anweisung const reader = new FileReader(); eine neue Instanz der Klasse 'FileReader' erzeugt.
In der darauf folgenden Zeile reader.addEventListener('load',...) wird dieser FileReader-Instanz ein Eventlistener für das Load-Event zugewiesen.
Durch die Anweisung reader.readAsDataURL(files[fileIndex].getSource().getSource()); wird der Inhalt des Bildes (die binären Rohdaten) ausgelesen und im base64-Format in der Variablen reader.result abgelegt. Am Ende des Lesevorgangs löst das Load-Event einen Aufruf des Load-Eventlisteners aus, in dem das src-Attribut nun nicht mit einer Bildadresse gefüllt wird, sondern mit den im base64-Format gespeicherten Bilddaten. Dies hat den Effekt, dass das Bild vom Browser angezeigt wird.
Der Pluploader sendet die ausgewählten Dateien (oder genauer gesagt einen HTML-Multipart-Request) an die serverseitige Datei 'receiver.php'.
<?php $targetDir = 'uploads/' . date("Y-m-d_H.i"); if (!file_exists($targetDir)) { @mkdir($targetDir); } $fileName = $_FILES["file"]["name"]; $filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName; move_uploaded_file($_FILES["file"]["tmp_name"], $filePath); ?>
Dieses PHP-Script erstellt im Ordner 'uploads' für jeden Upload einen Unterordner, dessen Name sich aus dem Tagesdatum und der Uhrzeit zusammensetzt, also beispielsweise '2023-09-22_15.33'. In diesem Unterordner werden dann alle hochgeladenen Dateien mit ihrem ursprünglichen Dateinamen gespeichert.
Auch in dieses PHP-Script ist das Login-System noch zu integrieren, wenn es im Internet zum Einsatz kommen soll.