jQuery 如何在表单中的 input type="file" 中添加图像并在同一表单上提交它们后生成缩略图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16500848/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 17:15:29  来源:igfitidea点击:

How to generate a thumbnail image after adding an image inside an input type="file" in a form and submitting them both on the same form

javascriptjqueryajaxhtml

提问by Alessandro Incarnati

I have a form which allows the user to upload a picture. After the user has submitted the form, I'd like to generate on the front-end a thumbnail for each picture and then store it on server.

我有一个允许用户上传图片的表单。用户提交表单后,我想在前端为每张图片生成一个缩略图,然后将其存储在服务器上。

For security reason it's not possible to alter the value of a file input field, so how could I send to server some thumbnails images generated on the front-end in js?

出于安全原因,无法更改文件输入字段的值,那么如何将前端在 js 中生成的一些缩略图图像发送到服务器?

Is it possible on front-end to generate a thumbnail from the image set in the input file field before form submit? And then submitting both at same time?

在表单提交之前,前端是否可以从输入文件字段中设置的图像生成缩略图?然后同时提交?

回答by che-azeh

I found This simpler yet powerful tutorial. It simply creates an imgelement and, using the fileReader object, assigns its source attribute as the value of the form input

我发现这个更简单但功能强大的教程。它只是创建一个img元素,并使用 fileReader 对象,将其源属性分配为表单输入的值

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...">

回答by Alessandro Incarnati

After a better search online I found the answer to my question.

在网上更好地搜索后,我找到了我的问题的答案。

It is possible to combine canvastogether with the File API.

可以将canvasFile API结合在一起。

Try to upload any picture in the demo below and see that a new generated thumbnail will appear on the right side of the form.

尝试上传下面演示中的任何图片,然后看到新生成的缩略图会出现在表单的右侧。

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

演示: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]);     
}

A good answer has been given by DerekR to this question:

DerekR 对这个问题给出了很好的答案:

How to upload image into HTML5 canvas

如何将图像上传到 HTML5 画布

回答by DanielM

TL;DR: See the JSFiddle

TL;DR:见 JSFiddle

As I wanted to upload images via an API and show a preview of the image (two things that actually lended themselves well to each other), I came up with this:

因为我想通过 API 上传图像并显示图像的预览(两件事实际上非常适合彼此),所以我想出了这个:

(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);

The two elements needed for the preview to work are:

预览工作所需的两个元素是:

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

Snippet Follows:

片段如下:

(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>

回答by Smitty

Thought it might be worth adding a more contemporary answer and citing MDN Web Docs.

认为可能值得添加一个更现代的答案并引用MDN Web Docs

You can add an event listener for "change" on the input element and then show a thumbnail of the selected image by accessing the file list through this.files(as shown in an MDN examples). Here is a recent implementation of mine. uploadWatermark is an <input type="file></input>

您可以在输入元素上为“更改”添加一个事件侦听器,然后通过访问文件列表来this.files显示所选图像的缩略图(如 MDN 示例中所示)。这是我最近的一个实现。上传水印是一个<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);
  }
  
});

回答by Pithikos

Building on top of what Allesandro wrote to something more pragmatic.

建立在 Allesandro 写的更务实的东西之上。

The function takes a file from the File API and tries to fit it in the boundBox while preserving the aspect ratio. Nothing is drawn, but instead you get back a Promisethat spits the dataUrl generated.

该函数从 File API 获取一个文件,并尝试将其放入 boundBox 中,同时保留纵横比。没有绘制任何内容,但您会返回一个Promise吐出生成的 dataUrl 的。

// Creates a thumbnail fitted insize the boundBox (w x h)
generateThumbnail(file, boundBox){
  if (!boundBox || boundBox.length != 2){
    throw "You need to give the boundBox"
  }
  var scaleRatio = Math.min(...boundBox) / Math.max(file.width, file.height)
  var reader = new FileReader();
  var canvas = document.createElement("canvas")
  var ctx = canvas.getContext('2d');

  return new Promise((resolve, reject) => {
    reader.onload = function(event){
        var img = new Image();
        img.onload = function(){
            var scaleRatio = Math.min(...boundBox) / Math.max(img.width, img.height)
            let w = img.width*scaleRatio
            let h = img.height*scaleRatio
            canvas.width = w;
            canvas.height = h;
            ctx.drawImage(img, 0, 0, w, h);
            return resolve(canvas.toDataURL(file.type))
        }
        img.src = event.target.result;
    }
    reader.readAsDataURL(file);
  })
}

It can be used like below

它可以像下面一样使用

generateThumbnail(file, [300, 300]).then(function(dataUrl){
    console.log(dataUrl)
})