web-dev-qa-db-fra.com

Comment générer une image miniature après avoir ajouté une image à l'intérieur d'un type d'entrée = "fichier" dans un formulaire et les avoir tous deux soumis sur le même formulaire

J'ai un formulaire qui permet à l'utilisateur de télécharger une image . Une fois que l'utilisateur a soumis le formulaire, j'aimerais générer au recto une vignette pour chaque image, puis la stocker sur le serveur.

Pour des raisons de sécurité, il n'est pas possible de modifier la valeur d'un champ de saisie de fichier. Comment envoyer au serveur des images miniatures générées en mode frontal au format js? 

Est-il possible sur le serveur frontal de générer une vignette à partir de l'image définie dans le champ du fichier d'entrée avant la soumission du formulaire? Et puis soumettre les deux en même temps?

26

J'ai trouvé Ce tutoriel simple mais puissant . Il crée simplement un élément img et, à l'aide de l'objet fileReader, affecte son attribut source comme valeur de l'entrée de formulaire

function previewFile() {
  var preview = document.querySelector('img');
  var file    = document.querySelector('input[type=file]').files[0];
  var reader  = new FileReader();

  reader.onloadend = function () {
    preview.src = reader.result;
  }

  if (file) {
    reader.readAsDataURL(file);
  } else {
    preview.src = "";
  }
}
<input type="file" onchange="previewFile()"><br>
<img src="" height="200" alt="Image preview...">

53
che-azeh

Après une meilleure recherche en ligne, j'ai trouvé la réponse à ma question.

Il est possible de combiner canvas avec l'API File.

Essayez de télécharger n'importe quelle image de la démo ci-dessous et voyez qu'une nouvelle vignette générée apparaîtra à droite du formulaire.

DEMO:http://jsfiddle.net/a_incarnati/fua75hpv/

function handleImage(e){
    var reader = new FileReader();
    reader.onload = function(event){
        var img = new Image();
        img.onload = function(){
            canvas.width = img.width;
            canvas.height = img.height;
            ctx.drawImage(img,0,0);
        }
        img.src = event.target.result;
    }
    reader.readAsDataURL(e.target.files[0]);     
}

DerekR a donné une bonne réponse à cette question:

Comment télécharger une image sur une toile HTML5

11

TL; DR: Voir le JSFiddle

Comme je voulais télécharger des images via une API et afficher un aperçu de l'image (deux éléments qui se prêtaient bien l'un pour l'autre), j'ai proposé ceci:

(function(angular) {
    angular
        .module('app')
        .directive('inputFilePreview', [function() {

            var canvas, mapToModel, elementScope;

            /**
             * To be fired when the image has been loaded
             */
            var imageOnLoad = function(){
                canvas.width = this.width;
                canvas.height = this.height;
                canvas.getContext("2d").drawImage(this,0,0);
            };

            /**
             * To be fired when the FileReader has loaded
             * @param loadEvent {{}}
             */
            var readerOnLoad = function(loadEvent){
                var img = new Image();
                img.onload = imageOnLoad;
                img.src = loadEvent.target.result;
                if(mapToModel) {
                    setModelValue(elementScope, mapToModel, img.src);
                }
            };

            /**
             * This allows us to set the value of a model in the scope of the element (or global scope if the
             * model is an object)
             * @param scope {{}}
             * @param modelReference {string}
             * @param value {*}
             */
            var setModelValue = function(scope, modelReference, value) {
                // If the model reference refers to the propery of an object (eg. "object.property")
                if(~modelReference.indexOf('.')) {
                    var parts = modelReference.split('.', 2);
                    // Only set the value if that object already exists
                    if(scope.hasOwnProperty(parts[0])) {
                        scope[parts[0]][parts[1]] = value;
                        return;
                    }
                }
                scope[modelReference] = value;
            };

            /**
             * The logic for our directive
             * @param scope {{}}
             * @param element {{}}
             * @param attributes {{}}
             */
            var link = function(scope, element, attributes) {
                elementScope = scope;
                canvas = document.getElementById(attributes.inputFilePreview);
                if(attributes.hasOwnProperty('mapToModel')) {
                    mapToModel = attributes.mapToModel;
                }
                element.on('change', function(changeEvent) {
                    var reader = new FileReader();
                    reader.onload = readerOnLoad;
                    reader.readAsDataURL(changeEvent.target.files[0]);
                });
            };

            return {
                restrict: 'A',
                link: link
            };
        }]);
})(angular);

Les deux éléments nécessaires au bon fonctionnement de l'aperçu sont les suivants:

<canvas id="image-preview"></canvas>
<input type="file" data-input-file-preview="image-preview" data-map-to-model="image.file" />

Snippet Suit:

(function (angular) {
    angular.module('app', [])
        .directive('inputFilePreview', [function () {

        var canvas, mapToModel, elementScope;

        /**
         * To be fired when the image has been loaded
         */
        var imageOnLoad = function () {
            canvas.width = this.width;
            canvas.height = this.height;
            canvas.getContext("2d").drawImage(this, 0, 0);
        };

        /**
         * To be fired when the FileReader has loaded
         * @param loadEvent {{}}
         */
        var readerOnLoad = function (loadEvent) {
            var img = new Image();
            img.onload = imageOnLoad;
            img.src = loadEvent.target.result;
            if (mapToModel) {
                setModelValue(elementScope, mapToModel, img.src);
            }
        };

        /**
         * This allows us to set the value of a model in the scope of the element (or global scope if the
         * model is an object)
         * @param scope {{}}
         * @param modelReference {string}
         * @param value {*}
         */
        var setModelValue = function (scope, modelReference, value) {
            // If the model reference refers to the propery of an object (eg. "object.property")
            if (~modelReference.indexOf('.')) {
                var parts = modelReference.split('.', 2);
                // Only set the value if that object already exists
                if (scope.hasOwnProperty(parts[0])) {
                    scope[parts[0]][parts[1]] = value;
                    return;
                }
            }
            scope[modelReference] = value;
        };

        /**
         * The logic for our directive
         * @param scope {{}}
         * @param element {{}}
         * @param attributes {{}}
         */
        var link = function (scope, element, attributes) {
            elementScope = scope;
            canvas = document.getElementById(attributes.inputFilePreview);
            if (attributes.hasOwnProperty('mapToModel')) {
                mapToModel = attributes.mapToModel;
            }
            element.on('change', function (changeEvent) {
                var reader = new FileReader();
                reader.onload = readerOnLoad;
                reader.readAsDataURL(changeEvent.target.files[0]);
            });
        };

        return {
            restrict: 'A',
            link: link
        };
    }])
        .controller('UploadImageController', [
        '$scope',

    function ($scope) {

        $scope.image = {
            title: 'Test title'
        };

        $scope.send = function (data) {
            $scope.sentData = JSON.stringify(data, null, 2);
            return false;
        };
    }]);
})(angular);
canvas {
    max-height: 300px;
    max-width: 300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form data-ng-app="app" data-ng-controller="UploadImageController">
    <input data-ng-model="image.title" />
    <br />
    <canvas id="image-preview"></canvas>
    <br />
    <input type="file" data-input-file-preview="image-preview" data-map-to-model="image.file" />
    <br />
    <input type="submit" data-ng-click="send(image)" />
    
    <pre>{{sentData}}</pre>
</form>

3
DanielM

Je pense qu’il pourrait être intéressant d’ajouter une réponse plus contemporaine et de citer MDN Web Docs .

Vous pouvez ajouter un écouteur d'événement pour "changer" sur l'élément d'entrée, puis afficher une vignette de l'image sélectionnée en accédant à la liste de fichiers via this.files (comme indiqué dans les exemples de noms MDN). Voici une implémentation récente de la mienne. uploadWatermark est un <input type="file></input>

uploadWatermark.addEventListener('change', function(){
  const file = this.files[0];
  if (file.type.startsWith('image/')) {
    const img = document.createElement('img');
    const watermarkPreview = document.getElementById("uploaded-watermark");

    img.classList.add("prev-thumb");
    img.file = file;
    watermarkPreview.appendChild(img);

    const reader = new FileReader();
    reader.onload = (function(aImg) { return function(e) { aImg.src =   e.target.result; }})(img);
    reader.readAsDataURL(file);
  }
  
});

0
Smitty