Bilder ohne Formular hochladen, ohne auf Flash zurückzugreifen, am besten mehrere auf einmal. Das geht mit HTML 5 und einer Prise Javascript recht einfach.
Die Grundidee
Das Input-Element von HTML 5 kann mehrere Dateien hochladen. Daher wird das genutzt, aber versteckt und aktiviert, indem ein anderes Element angeklickt und dann dieses den click
-Event des File-Input-Elements auslöst. Nach der Dateiauswahl wird den change
-Event ausgelöst, an der Stelle können die Daten der Dateien abgefangen, einem FileReader
übergeben und dann an den Server gesendet werden.
Anleitung
Zuerst braucht es ein Datei-Input-Element. Da das nur mit aktiviertem Javascript funktionieren wird, füge ich das auch per JS ein:
var imgButtonInput = document.createElement("input"); imgButtonInput.className = "imgButtonInput"; imgButtonInput.type = "file"; imgButtonInput.multiple = "multiple"; imgButtonInput.names = "images"; imgButtonInput.accept = "image"; body.appendChild(imgButtonInput);
Das Input-Element als Formularelement ist aber zur Darstellung hier relativ ungeeignet. Deshalb wird das versteckt und ein alternatives Element hinzugefügt:
.imgButtonInput { position: absolute; left: -9999em }
var imgButton = document.createElement("span"); imgButton.innerHTML = "IMG"; body.appendChild(imgButton);
Ein Klick darauf soll das File-Input-Element auslösen:
document.querySelector('.imgButton').addEventListener('click', function(evt) imgButtonInput.click(); };
Dessen change-Event muss dann die Dateien dem Server übergeben:
imgButtonInput.addEventListener('change', function() { for (var i = 0; i < files.length; i++) { var f = files[i]; (function(f) { var reader = new FileReader(); reader.addEventListener("load", function(event) { object = {}; object.filename = f.name; object.data = event.target.result; var options = { method: 'post', url: '/file', data: object } snack.request(options, function (err, res) { if (err) { alert("error uploading file: " + err); } alert("file uploaded"); }); }); reader.readAsDataURL(f); })(f); } });
Ich benutze hier snack, um den Ajax-Request etwas zu verschönern, das geht mit jQuery fast äquivalent und der Code ohne Library sollte auch keine Problem sein.
Auf Serverseite, bei mir Ruby/Sinatra, müssen die Dateien dann nur noch entgegengenommen und gespeichert werden:
post '/file' do protected! data = params[:data] filename = params[:filename].gsub("..", "") data_index = data.index('base64') + 7 filedata = data.slice(data_index, data.length) decoded_image = Base64.decode64(filedata) target = File.join(settings.public_folder, filename) until ! File.exists?(target) if target.scan(".").size > 1 # assume the filename is a classical xy.abc target = target.reverse.sub('.','._').reverse else target = target + "_" end end file = File.new(target, "w+") file.write(decoded_image) target.gsub(settings.public_folder, "") end
Quellen:
- http://www.skuunk.com/2011/04/reading-ajax-xhr-file-uploads-in.html
- http://www.nickdesteffen.com/blog/file-uploading-over-ajax-using-html5
- https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
Alternativ kann man natürlich auch eine vorgefertigte Lösung wie den fine-uploader benutzen. Und sich damit rumärgern, von der Githubseite für die Installationsanweisung zur Webseite verwiesen zu werden, die wiederum auf die Downloadseite verweist, wo es aber nur die Bezahlversion gibt. Um dann das Github-Repo zu ziehen und festzustellen, dass das bescheuerte Build-System unter Ubuntu nicht funktioniert.