使用Javascript在上传前检查图像的宽度和高度

202

我有一个带有表单的JPS页面,用户可以在其中上传图像:

<div class="photo">
    <div>Photo (max 240x240 and 100 kb):</div>
    <input type="file" name="photo" id="photoInput" onchange="checkPhoto(this)"/>
</div>

我写了这个js:

function checkPhoto(target) {
    if(target.files[0].type.indexOf("image") == -1) {
        document.getElementById("photoLabel").innerHTML = "File not supported";
        return false;
    }
    if(target.files[0].size > 102400) {
        document.getElementById("photoLabel").innerHTML = "Image too big (max 100kb)";
        return false;
    }
    document.getElementById("photoLabel").innerHTML = "";
    return true;
}

这段代码可以检查文件类型和大小。现在我想检查图片的宽度和高度,但是我无法做到。
我已经尝试过使用target.files[0].width,但是返回undefined。用其他方法则返回0
你有什么建议吗?

11个回答

287

这个文件只是一个文件,你需要按照以下方式创建一个图像:

var _URL = window.URL || window.webkitURL;
$("#file").change(function (e) {
    var file, img;
    if ((file = this.files[0])) {
        img = new Image();
        var objectUrl = _URL.createObjectURL(file);
        img.onload = function () {
            alert(this.width + " " + this.height);
            _URL.revokeObjectURL(objectUrl);
        };
        img.src = objectUrl;
    }
});

示例: http://jsfiddle.net/4N6D9/1/

我想你知道这只是在几种浏览器中支持。大部分是火狐和谷歌,现在可能也包括opera。

P.S. URL.createObjectURL() 方法已从 MediaStream 接口 中删除。此方法已在2013年被弃用,并被赋予将流分配给 HTMLMediaElement.srcObject。旧方法已被删除,因为它不够安全,需要调用 URL.revokeOjbectURL() 来结束流。其他用户代理已经弃用(Firefox)或删除(Safari)了此功能。

有关更多信息,请参见此处


1
除非您使用 Safari 6.0,否则它绝对不会在 Safari 上运行。截至目前,6.0 是唯一支持文件 API 的版本。我认为苹果永远不会为 Windows 发布 6.0 版本。5.1.7 已经是很久以前的最新版本了。 - Seho Lee
它在IE10中可以工作,但似乎在IE9及以下版本中无法工作。这是因为IE9及以下版本不支持文件API(http://caniuse.com/#search=file%20api)。 - Michael Yagudaev
尽管这个例子在纯JavaScript中运行良好,但最好提供一个在Angular上运行的示例,因为Angular无法接受匿名函数。最好用箭头函数替换匿名函数。 - Wisarut Bholsithi
内容安全策略防止了这个解决方案。 - Mehdi

78

我认为你需要的完美答案是:

var reader = new FileReader();

//Read the contents of Image File.
reader.readAsDataURL(fileUpload.files[0]);
reader.onload = function (e) {

  //Initiate the JavaScript Image object.
  var image = new Image();

  //Set the Base64 string return from FileReader as source.
  image.src = e.target.result;

  //Validate the File Height and Width.
  image.onload = function () {
    var height = this.height;
    var width = this.width;
    if (height > 100 || width > 100) {
      alert("Height and Width must not exceed 100px.");
      return false;
    }
    alert("Uploaded image has valid Height and Width.");
    return true;
  };
};

一个很好的答案,因为它不需要使用createObjectUrl。可以改进为使用reader.result而不是e.target.result,并且应该使用reader.onloadend而不是onload结合reader.onerror来处理错误。 - Felipe
2
我尝试了一个大小为13 MB的图像文件的代码。在浏览器中处理并返回宽度/高度需要长达2秒的时间。然而,最佳答案可以在几毫秒内获取图像尺寸。 - Avatar
2
@Avatar,你应该说明是谁发布了最佳答案,因为最佳答案随时间变化。 - constantlyFlagged
1
好观点。我的意思是这个答案:https://dev59.com/F2ox5IYBdhLWcg3w5IVv#8904008 - Avatar

44

我同意。一旦它上传到用户浏览器可以访问的地方,那么获取大小就相当容易了。由于您需要等待图像加载,因此您需要钩入imgonload事件。

更新后的示例:


// async/promise function for retrieving image dimensions for a URL
function imageSize(url) {
    const img = document.createElement("img");

    const promise = new Promise((resolve, reject) => {
      img.onload = () => {
        // Natural size is the actual image size regardless of rendering.
        // The 'normal' `width`/`height` are for the **rendered** size.
        const width  = img.naturalWidth;
        const height = img.naturalHeight; 

        // Resolve promise with the width and height
        resolve({width, height});
      };

      // Reject promise on error
      img.onerror = reject;
    });

    // Setting the source makes it start downloading and eventually call `onload`
    img.src = url;

    return promise;
}

// How to use in an async function
(async() => {
  const imageUrl = 'http://your.website.com/userUploadedImage.jpg';
  const imageDimensions = await imageSize(imageUrl);

  console.info(imageDimensions); // {width: 1337, height: 42}
})();

旧的示例:

var width, height;

var img = document.createElement("img");
img.onload = function() {
    // `naturalWidth`/`naturalHeight` aren't supported on <IE9. Fallback to normal width/height
    // The natural size is the actual image size regardless of rendering.
    // The 'normal' width/height are for the **rendered** size.
    
    width  = img.naturalWidth  || img.width;
    height = img.naturalHeight || img.height; 
    
    // Do something with the width and height
}

// Setting the source makes it start downloading and eventually call `onload`
img.src = "http://your.website.com/userUploadedImage.jpg";

当您需要在加载之前特别使用它们进行纵横比计算以防止累积布局移位时,该怎么办? - nrmad

36

这是检查大小的最简单方法

let img = new Image()
img.src = window.URL.createObjectURL(event.target.files[0])
img.onload = () => {
   alert(img.width + " " + img.height);
}

检查特定的大小。以100 x 100为例。

let img = new Image()
img.src = window.URL.createObjectURL(event.target.files[0])
img.onload = () => {
   if(img.width === 100 && img.height === 100){
        alert(`Nice, image is the right size. It can be uploaded`)
        // upload logic here
        } else {
        alert(`Sorry, this image doesn't look like the size we wanted. It's 
   ${img.width} x ${img.height} but we require 100 x 100 size image.`);
   }                
}

3
请记得调用URL.revokeObjectURL()以避免内存泄漏。https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL - squirtgun

12
我认为如果你想在其他功能中使用它,这可能是上传文件的最简单方法。

async function getImageDimensions(file) {
    let img = new Image();
    img.src = URL.createObjectURL(file);
    await img.decode();
    let width = img.width;
    let height = img.height;
    return {
        width,
        height,
    }
  }

使用方法

 const {width, height } = await getImageDimensions(file)

假设您想要存储一张在肯尼亚拍摄的老虎图片。那么,您可以像这样使用它来上传到云存储并存储照片信息。

const addImage = async (file, title, location) => {
   const { width, height } = await getImageDimensions(file)
   
   const url = await uploadToCloudStorage(file) // returns storage url

   await addToDatabase(url, width, height, title, location)
}


5

    const ValidateImg = (file) =>{
        let img = new Image()
        img.src = window.URL.createObjectURL(file)
        img.onload = () => {
            if(img.width === 100 && img.height ===100){
                alert("Correct size");
                return true;
            }
            alert("Incorrect size");
            return true;
        }
    }


5

将该函数附加到文件类型输入的onchange方法上 /onchange="validateimg(this)"/。

   function validateimg(ctrl) { 
        var fileUpload = ctrl;
        var regex = new RegExp("([a-zA-Z0-9\s_\\.\-:])+(.jpg|.png|.gif)$");
        if (regex.test(fileUpload.value.toLowerCase())) {
            if (typeof (fileUpload.files) != "undefined") {
                var reader = new FileReader();
                reader.readAsDataURL(fileUpload.files[0]);
                reader.onload = function (e) {
                    var image = new Image();
                    image.src = e.target.result;
                    image.onload = function () {
                        var height = this.height;
                        var width = this.width;
                        if (height < 1100 || width < 750) {
                            alert("At least you can upload a 1100*750 photo size.");
                            return false;
                        }else{
                            alert("Uploaded image has valid Height and Width.");
                            return true;
                        }
                    };
                }
            } else {
                alert("This browser does not support HTML5.");
                return false;
            }
        } else {
            alert("Please select a valid Image file.");
            return false;
        }
    }

4
function validateimg(ctrl) {
    var fileUpload = $("#txtPostImg")[0];
    var regex = new RegExp("([a-zA-Z0-9\s_\\.\-:])+(.jpg|.png|.gif)$");
    if (regex.test(fileUpload.value.toLowerCase())) {
        if (typeof (fileUpload.files) != "undefined") {
            var reader = new FileReader();
            reader.readAsDataURL(fileUpload.files[0]);
            reader.onload = function (e) {
                var image = new Image();
                image.src = e.target.result;
                image.onload = function () {
                    var height = this.height;
                    var width = this.width;
                    console.log(this);
                    if ((height >= 1024 || height <= 1100) && (width >= 750 || width <= 800)) {
                        alert("Height and Width must not exceed 1100*800.");
                        return false;
                    }
                    alert("Uploaded image has valid Height and Width.");
                    return true;
                };
            }
        } else {
            alert("This browser does not support HTML5.");
            return false;
        }
    } else {
        alert("Please select a valid Image file.");
        return false;
    }
}

2
请尝试格式化完整的代码,并提供一些关于您代码所做的事情的描述。 - Zeeshan Adil

2
您可以在不显示图像的情况下执行预览步骤,这在所有浏览器上都受支持。以下 js 代码向您展示如何检查宽度和高度:
var file = e.target.files[0];
if (/\.(jpe?g|png|gif)$/i.test(file.name)) {
    var reader = new FileReader();
    reader.addEventListener("load", function () {
        var image = new Image();
        image.src = this.result as string;
        image.addEventListener('load', function () {
            console.log(`height: ${this.height}, width: ${this.width}`);
        });
                
    }, false);
            
    reader.readAsDataURL(file);
}

根据Mozilla文档

readAsDataURL方法用于读取指定的BlobFile的内容。当读取操作完成时,readyState变为DONE,并触发loadend事件。此时,result属性包含一个数据URL,表示文件的数据作为一个base64编码的字符串。

并且浏览器兼容性也已列出。

2
在我的情况下,我需要防止表单被提交,因此这里是适用于我的解决方案。 preventDefault将停止表单操作,然后我们在onload函数中检查图像的大小和尺寸。
如果一切正常,我们允许提交。
由于如果用户使用无效图像仍然尝试提交表单,则提交按钮将被禁用,因此我还必须在输入有效图像后重新启用提交按钮。
const validateMaxImageFileSize = (e) => {
  e.preventDefault();
  const el = $("input[type='file']")[0];

  if (el.files && el.files[0]) {
    const file = el.files[0];

    const maxFileSize = 5242880; // 5 MB
    const maxWidth = 1920;
    const maxHeight = 1080;

    const img = new Image();
    img.src = window.URL.createObjectURL(file);
    img.onload = () => {
      if (file.type.match('image.*') && file.size > maxFileSize) {
        alert('The selected image file is too big. Please choose one that is smaller than 5 MB.');
      } else if (file.type.match('image.*') && (img.width > maxWidth || img.height > maxHeight)) {
        alert(`The selected image is too big. Please choose one with maximum dimensions of ${maxWidth}x${maxHeight}.`);
      } else {
        e.target.nodeName === 'INPUT'
          ? (e.target.form.querySelector("input[type='submit']").disabled = false)
          : e.target.submit();
      }
    };
  }
};

$('form.validate-image-size').on('submit', validateMaxImageFileSize);
$("form.validate-image-size input[type='file']").on('change', validateMaxImageFileSize);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接