2325 Stimmen

Bild vor dem Upload im Browser verkleinern

Tutorial von Stefan Trost | Letztes Update am 08.10.2022 | Erstellt am 21.09.2016

In diesem Tutorial möchte ich euch zeigen, wie ihr ein Bild-Upload realisieren könnt, bei dem das Bild automatisch verkleinert wird. Das Besondere bei der Verkleinerung ist allerdings, dass die Verkleinerung direkt und ohne Zusatzsoftware im Browser des Nutzers auf Client-Seite stattfindet - nicht in einem serverseitigen PHP-Skript - und damit die Ressourcen unseres Servers geschont werden.

Für die Verkleinerung nutzen wir HTML5-Technologien wie beispielsweise FileReader und Canvas. In unserem Tutorial gehen wir davon aus, dass der Nutzer das Bild in einem Datei-Dialog auswählt - in den Tutorials HTML5 Canvas Upload per Ajax und HTML5 Canvas Upload per Formular habe ich beschrieben, wie ihr einen bereits vorhandenen Canvas als Bild an den Server schicken und dort speichern könnt. Dieses Tutorial baut auf diesen beiden Tutorials auf, es ist aber nicht zwingend erforderlich, dass man die beiden anderen Tutorials kennt.

Die HTML-Seite

Kommen wir zunächst zu unserem HTML. Im wesentlichen brauchen wir hier eigentlich nur das Input-Feld, um die Datei auszusuchen und das Formular, das wir zum Absenden benutzen.

<input id="inp_file" type="file">

<form method="post" action="">
  <input id="inp_img" name="img" type="hidden" value="">
  <input id="bt_save" type="submit" value="Upload">
</form>

Wichtig ist hier, dass das Input-Feld bewusst nicht innerhalb der Form-Elemente steht. Schließlich möchten wir nicht die komplette große ausgewählte Datei an den Server senden, sondern nur eine Verkleinerung davon. Diese Verkleinerung führen wir später mit JavaScript durch und speichern das Ergebnis im versteckten Feld "img" in unserem Formular, mit dem das Bild dann gesendet werden kann.

Das JavaScript

Unser JavaScript möchte ich in mehreren Teilen erklären, am Ende dieses Tutorials findet ihr aber noch die komplette HTML-Seite mit dem vollständigen Code zusammen.

document.getElementById('inp_file').addEventListener('change', fileChange, false);

Mit dieser Zeile fügen wir einen Event-Listener zu unserem File-Input hinzu. Bei jeder Änderung (also immer dann, wenn der Nutzer ein neues Bild auswählt), soll die Funktion fileChange() aufgerufen werden.

Den ersten Teil dieser Funktion sehen wir hier:

function fileChange(e) { 
  document.getElementById('inp_img').value = '';
	
  var file = e.target.files[0];

  if (file.type == "image/jpeg" || file.type == "image/png") {
    // ...
  } else {
    document.getElementById('inp_file').value = '';	
    alert('Bitte wählen Sie ein Bild im JPG- oder PNG-Format aus.');	
  }
}

Als erstes wird das versteckte Feld "img" (mit der ID "inp_img") zurückgesetzt. Damit möchten erreichen, dass auch wenn der Nutzer eine falsche Datei auswählt, trotzdem nach jeder Aktion das Feld leer ist. Anschließend holen wir uns mit e.target.files[0] ein HTML5 File Object, das wir in der Variable file speichern und nutzen können.

Mit file.type erhalten wir eine Information darüber, um was für ein Dateityp es sich handelt. Da wir nur JPG- und PNG-Bilder erlauben möchten, erstellen wir eine if-Abfrage und geben ansonsten eine Fehlermeldung aus.

Sollte der Dateityp passend sein, geht der Code hier weiter (das "..." im Beispiel oben):

var reader = new FileReader();  
reader.onload = function(readerEvent) {
  // ...
}
reader.readAsDataURL(file);

Wir wissen nun, dass es sich um ein richtiges Bildformat handelt und erstellen ein HTML5 FileReader-Objekt, das uns die Bilddatei auslesen kann. Die Datei übergeben wir zum Auslesen mit readAsDataURL(file), vorher haben wir mit reader.onload aber ein Reader-Event erstellt, mit dem wir definieren, was beim Auslesen passieren soll.

Als erstes erstellen wir hier ein Image-Objekt, dem wir wieder ein Onload-Event zuordnen:

var image = new Image();
image.onload = function(imageEvent) {	
  // ...
}
image.src = readerEvent.target.result;

Innerhalb dieses Onload-Events geschieht nun unsere tatsächliche Verkleinerung des Bildes:

var max_size = 300;
var w = image.width;
var h = image.height;
			
if (w > h) {  if (w > max_size) { h*=max_size/w; w=max_size; }
} else     {  if (h > max_size) { w*=max_size/h; h=max_size; } }
			
var canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
canvas.getContext('2d').drawImage(image, 0, 0, w, h);
				
if (file.type == "image/jpeg") {
   var dataURL = canvas.toDataURL("image/jpeg", 1.0);
} else {
   var dataURL = canvas.toDataURL("image/png");	
}
document.getElementById('inp_img').value = dataURL;

Mit der Variable max_size definieren wir, wie groß unser Bild maximal sein darf. Danach lesen wir die aktuelle Höhe und Breite des Bildes aus und speichern die Werte in den Variablen w und h. Als nächstes entscheiden wir abhängig davon, ob das Bild im Hochformat oder Querformat vorliegt, welche neue Dimensionen unser Bild proportional haben soll.

Sobald die Werte feststehen, erstellen wir ein Canvas-Objekt in der gewünschten Größe und zeichnen unser Bild darauf. Das ist der Moment, in dem die Verkleinerung stattfindet.

Als nächstes holen wir uns mit .toDataURL einen BASE64-kodierten String unseres Bildes, den wir dann anschließend in unserem versteckten Formular-Feld zum Absenden speichern. Da wir sowohl PNG- als auch JPG-Bilder unterstützen möchten, haben wir noch eine entsprechende Fallunterscheidung eingebaut. Mit dem Aufruf von canvas.toDataURL("image/jpeg", 1.0) benutzen wir die beste JPG-Qualität für unser Bild. Wenn wir den Wert 1.0 auf einen geringeren Wert setzen, wird das Ergebnis eine höhere JPG-Kompression und damit eine geringere Dateigröße sein. Wir können den Wert zum Beispiel auf 0.5 für eine mittlere Qualität, 0.1 für eine geringe Qualität oder jeden anderen Wert dazwischen setzen.

Der PHP-Code

Der PHP-Code soll die Formular-Daten empfangen und das gesendete Bild abspeichern. Der Code gleicht weitestgehend dem PHP-Code der beiden oben verlinkten Tutorials, eine genaue Erklärung findet ihr dort.

if (count($_POST) && (strpos($_POST['img'], 'data:image') === 0)) {
	
  $img = $_POST['img'];
  
  if (strpos($img, 'data:image/jpeg;base64,') === 0) {
      $img = str_replace('data:image/jpeg;base64,', '', $img);
      $ext = '.jpg';
  }
  if (strpos($img, 'data:image/png;base64,') === 0) {
      $img = str_replace('data:image/png;base64,', '', $img);
      $ext = '.png';
  }
  
  $img = str_replace(' ', '+', $img);
  $data = base64_decode($img);
  $file = 'uploads/img'.date("YmdHis").$ext;
  
  if (file_put_contents($file, $data)) {
      echo "<p>Das Bild wurde gespeichert als $file.</p>";
  } else {
      echo "<p>Das Bild konnte nicht gespeichert werden.</p>";
  }	
  
}

Der einzige Unterschied besteht darin, dass wir hier wieder eine Fallunterscheidung brauchen, um dem unterschiedlichen Format gerecht zu werden. Ansonsten wird in diesem Code wieder die BASE64-Kodierung rückgängig gemacht und das Bild auf dem Server im Ordner "uploads" unter dem aktuellen Datum gespeichert.

Die komplette Datei

Gerade im JavaScript-Bereich war die Erklärung in den Tutorial etwas zerstückelt. Daher möchte ich hier noch einmal die komplette Datei mit dem gesamten HTML, PHP und JavaScript-Code wiederholen.

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head><body>

<?php

if (count($_POST) && (strpos($_POST['img'], 'data:image') === 0)) {
	
  $img = $_POST['img'];
  
  if (strpos($img, 'data:image/jpeg;base64,') === 0) {
      $img = str_replace('data:image/jpeg;base64,', '', $img);
      $ext = '.jpg';
  }
  if (strpos($img, 'data:image/png;base64,') === 0) {
      $img = str_replace('data:image/png;base64,', '', $img);
      $ext = '.png';
  }
  
  $img = str_replace(' ', '+', $img);
  $data = base64_decode($img);
  $file = 'uploads/img'.date("YmdHis").$ext;
  
  if (file_put_contents($file, $data)) {
      echo "<p>Das Bild wurde gespeichert als $file.</p>";
  } else {
      echo "<p>Das Bild konnte nicht gespeichert werden.</p>";
  }	
  
}
					 
?>


<input id="inp_file" type="file">

<form method="post" action="">
  <input id="inp_img" name="img" type="hidden" value="">
  <input id="bt_save" type="submit" value="Upload">
</form>


<script>

  function fileChange(e) { 
     document.getElementById('inp_img').value = '';
	
     var file = e.target.files[0];

     if (file.type == "image/jpeg" || file.type == "image/png") {

        var reader = new FileReader();  
        reader.onload = function(readerEvent) {
  
           var image = new Image();
           image.onload = function(imageEvent) {	
              var max_size = 300;
              var w = image.width;
              var h = image.height;
			
              if (w > h) {  if (w > max_size) { h*=max_size/w; w=max_size; }
              } else     {  if (h > max_size) { w*=max_size/h; h=max_size; } }
			
              var canvas = document.createElement('canvas');
              canvas.width = w;
              canvas.height = h;
              canvas.getContext('2d').drawImage(image, 0, 0, w, h);
				
              if (file.type == "image/jpeg") {
                 var dataURL = canvas.toDataURL("image/jpeg", 1.0);
              } else {
                 var dataURL = canvas.toDataURL("image/png");	
              }
              document.getElementById('inp_img').value = dataURL;
           }
           image.src = readerEvent.target.result;
        }
        reader.readAsDataURL(file);
     } else {
        document.getElementById('inp_file').value = '';	
        alert('Bitte wählen Sie ein Bild im JPG- oder PNG-Format aus.');
     }
  }

  document.getElementById('inp_file').addEventListener('change', fileChange, false);
		
</script>

</body></html>

Die Datei kann man zum Beispiel unter dem Namen "resize_form_upload.php" speichern.

Mehrere Bilder auf einmal uploaden

Mit dem hier vorgestellte Code kann man lediglich ein Bild gleichzeitig hochladen. Falls ihr mehrere Bilder gleichzeitig erlauben möchtet, muss man den Code noch etwas anpassen. Ich habe diese Arbeit bereits für euch erledigt, ihr findet alles hier.

AntwortenPositivNegativDatumStimmen
88 Stimmen

Das ist einfach spitze. Vielen Dank für deine Arbeit und fürs Teilen mit der Welt. Hast mir sehr geholfen :-)
28.12.2018 um 11:31

AntwortenPositiv Negativ
88 Stimmen

Ich bedanke mich ebenfalls. Das Script ist genial.

You made my day!
25.02.2019 um 19:33

AntwortenPositiv Negativ
77 Stimmen

Ich finde das Script auch super - vielen Dank!

Wäre es vielleicht noch möglich irgendwie die Bildgrösse (var max_size = 300) als Variable der Funktion zu übergeben?

Viele Grüsse aus dem Süden, Walter
20.04.2019 um 15:50

AntwortenPositiv Negativ
77 Stimmen

Sehr sehr geiles Script. Danke.

Ich würde gerne die "Bilder verkleinern"-Funktion in FPDF nutzen.

Nur leider kriege ich es nicht hin das Bild in einer Variable an FPDF zu übergeben.

Mein Projekt ist das Erstellen einer PDF mit mehreren Bildern um diese dann per Mail etc. zu verschicken. Nur leider wird die PDF dann viel zu groß. Hast du einen Tipp?

Grüße

Patrick
19.05.2021 um 17:05

AntwortenPositiv Negativ
77 Stimmen

Kannst dich gerne über mein Kontaktformular bei mir melden:

sttmedia.de/kontakt
19.05.2021 um 20:35

Positiv Negativ
Antworten
00 Stimmen

Hallo super Arbeit, ich überlege gerade wie man da das GIF noch hinzufügt.

Eine Stelle ist mir klar

if (file.type == "image/jpeg" || 
    file.type == "image/png" || 
    file.type == "image/gif") {

aber weiter unten die Abfrage bekomme ich nicht hin bei

if (file.type == "image/jpeg")

und dem else ....

Habe probiert eine elseif dazu zu packen mit image/gif aber irgend wie geht er nur auf JPG oder PNG grrr
28.03.2025 um 12:46

AntwortenPositiv Negativ
00 Stimmen

Wenn du den Teil mit dem else erweitern willst, könntest du es so machen:

if (file.type == "image/jpeg") {
  var dataURL = canvas.toDataURL("image/jpeg", 1.0);
} else if (file.type == "image/png") {
  var dataURL = canvas.toDataURL("image/png");   
} else if (file.type == "image/gif") {
  var dataURL = canvas.toDataURL("image/gif");   
}

Alternativ ginge es auch mit switch:

switch (file.type) {
  case "image/jpeg":
    var dataURL = canvas.toDataURL("image/jpeg", 1.0);
    break;
  case "image/png":
    var dataURL = canvas.toDataURL("image/png");
    break;
  case "image/gif":
    var dataURL = canvas.toDataURL("image/gif");
    break;
}

Ist jetzt allerdings ungetestet. Funktioniert es so?
28.03.2025 um 17:30

Positiv Negativ
00 Stimmen

Hallo und Danke für die Mühe, leider nein !

Ich glaube dass es nicht geht weil "canvas" immer noch nicht GIF verarbeiten kann.

Oder dass man da einen ganz anderen Ansatz machen muss bei GIF was ich nicht verstehe oder weiß.

Immer wieder speichert er mir eine PNG bei diesem Verfahren.

Oben habe ich hinzugefügt

if (strpos($img, 'data:image/gif;base64,') === 0) {
  $img = str_replace('data:image/gif;base64,', '', $img);
  $ext = '.gif';
}

dann das zum Akzeptieren || file.type == "image/gif"

und dann deine zwei Vorschläge...
31.03.2025 um 09:07

Positiv Negativ
00 Stimmen

Da der Code ja im Browser des Nutzers ausgeführt wird, muss der entsprechende Browser das gewünschte Format unterstützen. Das kann sowohl vom verwendeten Browser (Chrome, Firefox etc) als auch von der Version des Browsers abhängen (vielleicht kommen in Zukunft mehr Formate hinzu).

Ich habe mal etwas recherchiert, Infos findest du zum Beispiel hier. Dort steht:

"Browsers are required to support image/png; many will support additional formats including image/jpeg and image/webp."

Heißt wirklich verlassen kannst du dich nur darauf, dass PNG supported wird, der Support anderer Formate hängt davon ab, ob der Browserhersteller dies zusätzlich unterstützen möchte.

Weiterhin findest du auf der Seite diesen Satz:

"If the file format is not specified, or if the given format is not supported, then the data will be exported as image/png. In other words, if the returned value starts with data:image/png for any other requested type, then that format is not supported."

Dass du ein PNG statt ein GIF erhälst liegt also daran, dass der Browser, den du verwendest kein GIF-Export unterstützt. Daher gibt er dir statt dem gewünschten Format PNG zurück.

Ich habe dann noch weiter recherchiert, welche Dateitypen unterstützt werden und keinen Browser mit GIF-Support finden können. Die meisten Browser supporten tatsächlich nur PNG und JPG, manche noch WEBP oder BMP.

Wenn du das Bild zwingend als GIF benötigst, wäre eine sichere Lösung also das Bild erstmal als PNG zu empfangen und dann anschließend erst auf dem Server (wo du die Kontrolle über alles hast) in ein GIF zu konvertieren (oder darauf zu warten, dass es unter Umständen in Zukunft Browser gibt, die auch GIFs unterstützen).

Sorry, dass ich dir die ungetestete Lösung geschrieben habe. Vor meiner Recherche bin ich davon ausgegangen, dass die Browser zumindest die gängigsten Dateiformate supporten und mir haben bisher immer nur PNG- und JPEG-Bilder gereicht.
01.04.2025 um 06:57

Positiv Negativ
00 Stimmen

Hallo und Danke für die Mühe

Alles kein Problem... ich Teste und lerne ja gerne immer weiter.

Im großen und ganzen habe ich das ja auch raus gefunden das nur PNG und JPEG von den meisten unterstützt werden.

Ich benutze Firefox win11 in der Aktuellen Version.

Sowie Edge und Crome zum Testen. All diese unterstützen kein GIF .

Wünsche dir noch alles gute.
04.04.2025 um 18:10

Positiv Negativ
Antworten
Antworten

Über den Autor

AvatarSoftware von Stefan Trost finden Sie auf sttmedia.de. Benötigen Sie eine individuelle Software nach Ihren eigenen Wünschen? Schreiben Sie uns: sttmedia.de/kontakt
Profil anzeigen

 

Ähnliche Themen

Android Splash Screen Tutorial

Tutorial | 0 Kommentare

Wichtiger Hinweis

Bitte beachten Sie: Die Beiträge auf askingbox.de sind Beiträge von Nutzern und sollen keine professionelle Beratung ersetzen. Sie werden nicht von Unabhängigen geprüft und spiegeln nicht zwingend die Meinung von askingbox.de wieder. Mehr erfahren.

Jetzt mitmachen

Stellen Sie Ihre eigene Frage oder schreiben Sie Ihren eigenen Artikel auf askingbox.de. So gehts.