44 Stimmen

Bild vor dem Upload im Browser verkleinern

Tutorial von Stefan Trost | Letztes Update am 17.05.2021 | 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
Avatar
00 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
Avatar
00 Stimmen

Ich bedanke mich ebenfalls. Das Script ist genial.

You made my day!
25.02.2019 um 19:33

AntwortenPositiv Negativ
Avatar
00 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
Avatar
00 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
Avatar
00 Stimmen

Kannst dich gerne über mein Kontaktformular bei mir melden:

sttmedia.de/kontakt
19.05.2021 um 20:35

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

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.