Wie soll ich ein Bild auf der Clientseite mit jcrop zuschneiden und hochladen?

Lesezeit: 9 Minuten

Wie soll ich ein Bild auf der Clientseite mit jcrop
Govinda Sakhare

Ich arbeite an einer Komponente, in der es ein HTML-Steuerelement zum Hochladen von Dateien gibt. Wenn Sie ein Bild mit dem Datei-Upload-Element auswählen, wird das Bild auf dem HTML5-Canvas-Element gerendert.

Hier ist JSFiddle mit Beispielcode: https://jsfiddle.net/govi20/spmc7ymp/

id=Ziel => Selektor für jcrop-Element
id=Foto => Selektor für fileupload-Element
id=Vorschau => Selektor für Canvas-Element
id=clear_selection => Selektor für eine Schaltfläche, die die Leinwand löschen würde

Verwendete JS-Bibliotheken von Drittanbietern:

<script src="./js/jquery.min.js"></script>
<script src="./js/jquery.Jcrop.js"></script>
<script src="./js/jquery.color.js"></script>

JCrop einrichten:

<script type="text/javascript">

jQuery(function($){
 
var api;

$('#target').Jcrop({
  // start off with jcrop-light class
  bgOpacity: 0.5,
  keySupport: false,
  bgColor: 'black',
  minSize:[240,320],
  maxSize:[480,640],
  onChange : updatePreview,
  onSelect : updatePreview, 
  height:160,
  width:120,
  addClass: 'jcrop-normal'
},function(){
  api = this;
  api.setSelect([0,0,240,320]);
  api.setOptions({ bgFade: true });
  api.ui.selection.addClass('jcrop-selection');
  });

});

Clear-Canvas-Event, das beim Click-Event des Clear-Buttons ausgelöst wird:

jQuery('#clear_selection').click(function(){
  $('#target').Jcrop({    
      
      setSelect: [0,0,0,0],
    });
});

Code, der das Bild auf HTML5 Canvas rendert:

function readURL(input) {
    
    if (input.files && input.files[0]) {
        var reader = new FileReader();
        reader.onload = function (e) {
            $('#target').attr('src', e.target.result);
            setProperties();       
        }
        reader.readAsDataURL(input.files[0]);
    }
}

function setProperties(){
   $('#target').Jcrop({         
              setSelect: [0,0,240,320]
        }); 
}
$("#photograph").change(function(){
    readURL(this);     
});

Code zum Zuschneiden und Rendern eines Bildes auf der Leinwand:

    var canvas = document.getElementById('preview'),
    context = canvas.getContext('2d');

    make_base();
    function updatePreview(c) {
        console.log("called");
        if(parseInt(c.w) > 0) {
            // Show image preview
            var imageObj = $("#target")[0];
            var canvas = $("#preview")[0];
            var context = canvas.getContext("2d");
            context.drawImage(imageObj, c.x, c.y, c.w, c.h, 0, 0, canvas.width, canvas.height);
        }
    };

    function make_base() {
        console.log("make_base called");
        var base_image = new Image();
        base_image.src="";
        base_image.onload = function () {
            context.drawImage(base_image, 0, 0);
        }
    }

Hier sind eine Reihe von Problemen, denen ich mit dem obigen Setup gegenüberstehe:

  1. Die updatePreview-Funktion wird bei der Auswahl nicht aufgerufen, daher wird die Leinwand nicht gerendert.
  2. Das Ernteauswahlfeld kann nicht gezogen werden (ich verwende Bootstrap-CSS, ich vermute, dass dies auf fehlende/nicht übereinstimmende Abhängigkeiten zurückzuführen ist).
  3. Leinwand ist HTML5 -Element, was bedeutet, dass der Endbenutzer über eine verfügen muss HTML5 kompatiblen Browser arbeite ich an einer App, die Millionen von Benutzern hat. Benutzer zu zwingen, den neuesten Browser zu verwenden, ist keine praktikable Option. Was sollte hier der Fallback-Mechanismus sein?

Wie soll ich ein Bild auf der Clientseite mit jcrop
Seepferdchen

Hier ist grundlegender HTML 5-Code:

https://jsfiddle.net/zm7e0jev/

Dieser Code beschneidet das Bild, zeigt eine Vorschau und setzt den Wert eines Eingabeelements auf das base64-codierte beschnittene Bild.

Sie können die Bilddatei in PHP folgendermaßen abrufen:

//File destination
$destination = "/folder/cropped_image.png";
//Get convertable base64 image string
$image_base64 = $_POST["png"];
$image_base64 = str_replace("data:image/png;base64,", "", $image_base64);
$image_base64 = str_replace(" ", "+", $image_base64);
//Convert base64 string to image data
$image = base64_decode($image_base64);
//Save image to final destination
file_put_contents($destination, $image);

Das Senden einer base64-Bildzeichenfolge als Post-Variable hat seine Server-Post-Größenbeschränkungen und die base64-Codierung macht die Dateigröße des zugeschnittenen Bilds noch größer (~ 33 %) als die Rohdaten des zugeschnittenen Bilds, wodurch das Hochladen noch länger dauert.

So legen Sie die Größenbeschränkung für Posts fest: Was ist die Größenbeschränkung einer Post-Anforderung?

Beachten Sie, dass ein erhöhtes Post-Size-Limit beispielsweise für einen DoS-Angriff missbraucht werden kann.

Stattdessen schlage ich vor, das beschnittene Base64-Bild in einen Datenblob zu konvertieren und es dann beim Absenden als Datei zum Formular hinzuzufügen:

https://jsfiddle.net/g3ysk6sf/

Dann können Sie die Bilddatei in PHP auf folgende Weise abrufen:

//File destination
$destination = "/folder/cropped_image.png";
//Get uploaded image file it's temporary name
$image_tmp_name = $_FILES["cropped_image"]["tmp_name"][0];
//Move temporary file to final destination
move_uploaded_file($image_tmp_name, $destination);

Aktualisieren:

FormData() wird in IE10 nur teilweise unterstützt und in älteren Versionen von IE nicht unterstützt

Daher schlage ich vor, die base64-Zeichenfolge als Fallback zu senden, obwohl dies zu Problemen mit größeren Bildern führt, sodass die Dateigröße überprüft und ein Fehler-Popup angezeigt werden muss, wenn das Bild eine bestimmte Größe überschreitet.

Ich werde ein Update mit dem Fallback-Code unten posten, wenn ich es zum Laufen gebracht habe.

Aktualisierung 2:

Ich habe einen Fallback für IE10 und darunter hinzugefügt:

https://jsfiddle.net/oupxo3pu/

Die einzige Einschränkung ist die Bildgröße, die bei Verwendung von IE10 und niedriger gesendet werden kann. Falls die Bildgröße zu groß ist, gibt der js-Code einen Fehler aus. Die maximale Größe für Post-Werte ist bei jedem Server unterschiedlich, der js-Code verfügt über eine Variable zum Festlegen der maximalen Größe.

Der folgende PHP-Code ist so angepasst, dass er mit dem obigen Fallback funktioniert:

//File destination
$destination = "/folder/cropped_image.png";
if($_POST["png"]) {//IE10 and below
    //Get convertable base64 image string
    $image_base64 = $_POST["png"];
    $image_base64 = str_replace("data:image/png;base64,", "", $image_base64);
    $image_base64 = str_replace(" ", "+", $image_base64);
    //Convert base64 string to image data
    $image = base64_decode($image_base64);
    //Save image to final destination
    file_put_contents($destination, $image);
} else if($_FILES["cropped_image"]) {//IE11+ and modern browsers
    //Get uploaded image file it's temporary name
    $image_tmp_name = $_FILES["cropped_image"]["tmp_name"][0];
    //Move temporary file to final destination
    move_uploaded_file($image_tmp_name, $destination);
}

Es gibt noch keinen Fallback-Code für das Canvas-Element, ich schaue mir das an.

Die Beschränkung der Beitragsgröße im Fallback für ältere Browser ist einer der Gründe, warum ich selbst die Unterstützung für ältere Browser eingestellt habe.

Aktualisierung 3:

Der Fallback, den ich für das Canvas-Element im IE8 empfehle:

http://flashcanvas.net/

Es unterstützt alle Canvas-Funktionen, die der Zuschneidecode benötigt.

Denken Sie daran, es erfordert Flash. Es gibt ein Leinwand-Fallback (explorercanvas), das kein Flash benötigt, aber die Funktion toDataURL() nicht unterstützt, die wir zum Speichern unseres zugeschnittenen Bildes benötigen.

  • An meinem Telefon bin ich derzeit in ein oder zwei Stunden zu Hause :/ Mit dem Zug zu reisen ist eine schrecklich langsame Erfahrung ….

    – Seepferdchen

    10. Januar 2016 um 17:27 Uhr

  • Auch das Hochladen eines clientseitig zugeschnittenen Bildes ist eine knifflige Sache, die mich etwas Arbeit gekostet hat. Im ersten Moment habe ich es zu einer Base64-Zeichenfolge gemacht, die jedoch aufgrund ihrer Länge beim Senden auf Probleme gestoßen ist. Danach habe ich Code hinzugefügt, um die base64-Zeichenfolge in ein Bilddatei-Blob zu verwandeln, und dann habe ich das mit einem HTML5-Code zum Formular hinzugefügt (damit auch ein Fallback erforderlich wäre). Ich beendete das Löschen älterer Browser alle zusammen im Projekt, weil ich immer wieder auf diese Einschränkungen stieß und das Erstellen von Fallbacks für jede Einschränkung zu viel Zeit und Arbeit kostete.

    – Seepferdchen

    10. Januar 2016 um 17:33 Uhr

  • Zurück, gib mir eine Minute, um Ajs Geige zu machen.

    – Seepferdchen

    10. Januar 2016 um 21:48 Uhr

  • Okay, werde das von dir gepostete Zeug ausprobieren und dich über das Ergebnis informieren.

    – Govinda Sakhare

    11. Januar 2016 um 4:57 Uhr

  • funktioniert nicht mehr mit firefox oder edge. ersetzen Sie die context.draw durch diese: ” if (coords.w !== 0 && coords.h !== 0) { context.drawImage(imageObj, coords.x, coords.y, coords.w, coords.h, 0, 0, Leinwand.Breite, Leinwand.Höhe); }”

    – Graf Murphy

    4. Januar 2017 um 20:32 Uhr

1647210069 305 Wie soll ich ein Bild auf der Clientseite mit jcrop
Tatarisieren

Die Antwort von Seahorsepip ist fantastisch. Ich habe viele Verbesserungen an der Nicht-Fallback-Antwort vorgenommen.

http://jsfiddle.net/w1Lh4w2t/

Ich würde empfehlen, dieses seltsame versteckte PNG-Ding nicht zu machen, wenn ein Bildobjekt genauso gut funktioniert (solange wir keine Fallbacks unterstützen).

var jcrop_api;
var canvas;
var context;
var image;
var prefsize;

Aber selbst dann ist es besser, wenn Sie diese Daten am Ende aus der Leinwand holen und erst am Ende in dieses Feld einfügen.

function loadImage(input) {
  if (input.files && input.files[0]) {
    var reader = new FileReader();
    reader.onload = function(e) {
      image = new Image();
      image.src = e.target.result;
      validateImage();
    }
    reader.readAsDataURL(input.files[0]);
  }
}

Wenn Sie jedoch mehr Funktionen als nur das Zuschneiden wünschen, hängen wir das jcrop an eine eingefügte Leinwand an (die wir mit dem jcrop beim Aktualisieren zerstören). Wir können ganz einfach alles tun, was wir mit einer Leinwand tun können, dann validateImage() erneut und das aktualisierte Bild an Ort und Stelle sichtbar machen.

function validateImage() {
  if (canvas != null) {
    image = new Image();
    image.src = canvas.toDataURL('image/png');
  }
  if (jcrop_api != null) {
    jcrop_api.destroy();
  }
  $("#views").empty();
  $("#views").append("<canvas id=\"canvas\">");
  canvas = $("#canvas")[0];
  context = canvas.getContext("2d");
  canvas.width = image.width;
  canvas.height = image.height;
  context.drawImage(image, 0, 0);
  $("#canvas").Jcrop({
    onSelect: selectcanvas,
    onRelease: clearcanvas,
    boxWidth: crop_max_width,
    boxHeight: crop_max_height
  }, function() {
    jcrop_api = this;
  });
  clearcanvas();
}

Beim Absenden senden wir dann alle ausstehenden Operationen wie applyCrop() oder applyScale() und fügen Daten in versteckte Felder für Fallback-Sachen ein, wenn wir diese Dinge benötigen. Wir haben dann ein System, mit dem wir die Leinwand einfach in irgendeiner Weise ändern können, und wenn wir dann die Leinwanddaten senden, werden sie ordnungsgemäß gesendet.

function applyCrop() {
  canvas.width = prefsize.w;
  canvas.height = prefsize.h;
  context.drawImage(image, prefsize.x, prefsize.y, prefsize.w, prefsize.h, 0, 0, canvas.width, canvas.height);
  validateImage();
}

Die Leinwand wird zu einer Div-Ansicht hinzugefügt.

 <div id="views"></div>

Um die angehängte Datei in PHP (drupal) abzufangen, habe ich so etwas verwendet:

    function makeFileManaged() {
        if (!isset($_FILES['croppedfile']))
            return NULL;
        $path = $_FILES['croppedfile']['tmp_name'];
        if (!file_exists($path))
            return NULL;
        $result_filename = $_FILES['croppedfile']['name'];
        $uri = file_unmanaged_move($path, 'private://' . $result_filename, FILE_EXISTS_RENAME);
        if ($uri == FALSE)
            return NULL;
        $file = File::Create([
                    'uri' => $uri,
        ]);
        $file->save();
        return $file->id();
    }

  • Versuchen Sie meistens das jfiddle, sollte voll funktionsfähig sein, außer dem Upload auf das jfiddle-Ding.

    – Tatarisieren

    12. Juni 2016 um 13:20 Uhr

  • Zwei Dinge 1) Ich lade ein Bild mit dem Upload-Controller hoch, bootstrap modal kein Bild zeigt, dann klicke ich auf crop button jetzt zeigt es das Bild. Wenn ich jetzt das Modal schließe und das Bild erneut hochlade, wird ein altes Bild angezeigt.

    – Govinda Sakhare

    14. Juni 2016 um 8:18 Uhr

  • Das Bild wird von der Leinwand getrennt und die Leinwand gespeichert. Das hochgeladene Bild wird aus der geänderten Leinwand erstellt. Sie müssen entweder das Bild aus dem Bootstrap-Modal aktualisieren, indem Sie die Daten zurück an die Seite senden und dort die Leinwand neu erstellen, oder Sie müssen das geänderte Bild direkt aus dem Bootstrap-Modal senden.

    – Tatarisieren

    15. Juni 2016 um 0:38 Uhr

  • Zur Antwort hinzugefügt. Hoffe, es hilft, obwohl sich dort die Datei befindet und darüber hinaus den Rahmen der ursprünglichen Frage etwas zu weit überschreitet. Es wird hochgeladen und in die temporären Dateien des Webservers eingefügt und muss aus dem POST abgerufen werden, indem auf das Zeug in $_FILES zugegriffen wird. Das Debuggen von PHP hat sehr geholfen.

    – Tatarisieren

    24. September 2017 um 20:02 Uhr

  • @Tatarize danke für die Codes, lass mich sie studieren und hoffentlich kann ich sie in ein Bild umwandeln und in einem Ordner speichern. Nochmals vielen Dank für die Beantwortung meiner Fragen 🙂

    – gomesh munda

    24. September 2017 um 20:06 Uhr

999190cookie-checkWie soll ich ein Bild auf der Clientseite mit jcrop zuschneiden und hochladen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy