
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:
- Die updatePreview-Funktion wird bei der Auswahl nicht aufgerufen, daher wird die Leinwand nicht gerendert.
- Das Ernteauswahlfeld kann nicht gezogen werden (ich verwende Bootstrap-CSS, ich vermute, dass dies auf fehlende/nicht übereinstimmende Abhängigkeiten zurückzuführen ist).
- 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?

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.

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();
}
9991900cookie-checkWie soll ich ein Bild auf der Clientseite mit jcrop zuschneiden und hochladen?yes