从URL获取远程图片的宽度和高度

98
所以这个警告给出了宽度和高度的未定义值。我认为来自img.onload计算的图像的w和h值没有被传递到要返回的值中,或者可能在onload计算它们之前返回w和h的值:
function getMeta(url){
 var w; var h;
 var img=new Image;
 img.src=url;
 img.onload=function(){w=this.width; h=this.height;};
 return {w:w,h:h}    
}

// "http://snook.ca/files/mootools_83_snookca.png" //1024x678
// "http://shijitht.files.wordpress.com/2010/08/github.png" //128x128

var end = getMeta("http://shijitht.files.wordpress.com/2010/08/github.png");
var w = end.w;
var h = end.h;
alert(w+'width'+h+'height');

如何使警报框显示正确的宽度和高度?

http://jsfiddle.net/YtqXk/


3
无法运行的原因是,当你返回宽度和高度值时,它们尚未确定。你需要在“onload”回调函数内部处理结果。你应该将回调函数作为第二个参数传递给你的“getMeta”函数。 - powerbuoy
6个回答

167

使用JavaScript获取图片尺寸

为了从一张图片中读取数据,你需要确保它已经被加载。下面是基于回调的方法和两个基于Promise的解决方案:

回调函数

const getMeta = (url, cb) => {
  const img = new Image();
  img.onload = () => cb(null, img);
  img.onerror = (err) => cb(err);
  img.src = url;
};

// Use like:
getMeta("https://istack.dev59.com/qCWYU.webp", (err, img) => {
  console.log(img.naturalWidth, img.naturalHeight);
});

使用load事件监听器(Promise):

const getMeta = (url) =>
  new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = (err) => reject(err);
    img.src = url;
  });

// Usage example: 
;(async() => {
  const img = await getMeta('https://istack.dev59.com/qCWYU.webp');
  console.dir(img.naturalHeight + ' ' + img.naturalWidth);
})();

使用HTMLImageElement.decode()(Promise)

const getMeta = async (url) => {
  const img = new Image();
  img.src = url;
  await img.decode();  
  return img
};

// Usage example:
getMeta('https://istack.dev59.com/qCWYU.webp').then(img => {
  console.dir(img.naturalHeight +' '+ img.naturalWidth);
});


7
由于宽度和高度并未返回,我应该如何让它们在函数外被返回以便使用? - Wonka
3
@Wonka 在 .load 函数内部调用一些函数或设置一个 setInterval,因为当你的代码被读取并执行时,图片仍在加载中:setInterval 将会持续检查,直到图片加载完成并自我清除,就像这个演示:http://jsfiddle.net/roXon/SN2t6/1/ - Roko C. Buljan
2
@Wonka 看这里关于图片加载后立即调用函数的方法: http://jsfiddle.net/roXon/SN2t6/4/ - Roko C. Buljan
请注意,在IE11中,SVG图像的宽度和高度返回为“0”。 - Sam
@RokoC.Buljan,jQuery 部分的一段代码抛出错误:Uncaught TypeError: url.indexOf is not a function。我认为有一个小错误潜入其中,即 img 属性和监听器被放在同一个对象中。请参见此答案 - almaceleste
如果不在同一源上,即使添加了“img.crossOrigin=true”,它也会返回CORS错误。是否有一种方法可以更改此请求的MIME类型或以其他方式向纯JPG请求添加标头? - lowcrawler

35

只需像这样将回调函数作为参数传递:

function getMeta(url, callback) {
    const img = new Image();
    img.src = url;
    img.onload = function() { callback(this.width, this.height); }
}
getMeta(
  "http://snook.ca/files/mootools_83_snookca.png",
  (width, height) => { alert(width + 'px ' + height + 'px') }
);


1
谢谢,这对我帮助很大。 - Michael
请注意,在IE11中,SVG图像的宽度和高度返回为“0”。 - Sam

28

ES6

使用async/await,您可以像顺序方式一样执行下面的getMeta函数,并且可以按照以下方式使用它(几乎与您问题中的代码相同(我添加了await关键字并将变量end更改为img,并将var更改为let关键字)。您需要仅从async函数(run)运行getMeta 时使用await

function getMeta(url) {
    return new Promise((resolve, reject) => {
        let img = new Image();
        img.onload = () => resolve(img);
        img.onerror = () => reject();
        img.src = url;
    });
}

async function run() {

  let img = await getMeta("http://shijitht.files.wordpress.com/2010/08/github.png");

  let w = img.width;
  let h = img.height; 

  size.innerText = `width=${w}px, height=${h}px`;
  size.appendChild(img);
}

run();
<div id="size" />

Rxjs

const { race, fromEvent, map, mergeMap, of } = rxjs;

function getMeta(url) {
    return of(url).pipe(
        mergeMap((path) => {
            const img = new Image();
            let load = fromEvent(img, 'load').pipe(map(_=> img))
            let error = fromEvent(img, 'error').pipe(mergeMap((err) => throwError(() => err)));
            img.src = path;
            
            return race(load, error);
        })
    );
}


let url = "http://shijitht.files.wordpress.com/2010/08/github.png";

getMeta(url).subscribe(img=> { 
  let w = img.width;
  let h = img.height; 

  size.innerText = `width=${w}px, height=${h}px`;
  size.appendChild(img);
}, e=> console.log('Load error'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.5.2/rxjs.umd.min.js" integrity="sha512-wBEi/LQM8Pi08xK2jwHJNCiHchHnfcJao0XVQvkTGc91Q/yvC/6q0xPf+qQr54SlG8yRbRCA8QDYrA98+0H+hg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<div id="size" />


谢谢。我使用了Promise.all()一次性获取所有值。 - Bimal Grg

13

img.onload函数中的wh变量与getMeta()函数中的不在同一个作用域中。解决方法之一如下:

Fiddle: http://jsfiddle.net/ppanagi/28UES/2/

function getMeta(varA, varB) {
    if (typeof varB !== 'undefined') {
       alert(varA + ' width ' + varB + ' height');
    } else {
       var img = new Image();
       img.src = varA;
       img.onload = getMeta(this.width, this.height);
    }
}


getMeta("http://snook.ca/files/mootools_83_snookca.png");

谢谢,这个程序的功能是正常的,只是获取信息的时间有点长。 - Wonka
1
如果图像尚未存储在浏览器缓存中,则此解决方案将无法正常工作。您必须在getMeta中接受回调函数,该函数将在onload事件触发时被调用(http://jsfiddle.net/28UES/)。 - Russ Ferri
@Zack 我看到更新了,解决了缓存问题。但是你能展示一下如何在函数外部访问宽度和高度吗?我需要该函数返回自身外的值。 - Wonka
@Wonka 在下载图片之前是不可能知道它的宽度和高度的。这是异步发生的(https://dev59.com/CHE85IYBdhLWcg3wnU4d),因此getMeta函数无法直接返回尺寸。通常的解决方法是将回调函数传递给getMeta,该函数在加载图像后对尺寸执行有用的操作。 - Russ Ferri
getMeta() 函数之外的任何代码都将在图像尺寸可用之前被调用。因此,正如 Ferri 指出的那样,您需要为图像的 onload 事件使用回调函数。 - Panagiotis Panagi
显示剩余3条评论

0

使用jQuery获取图像尺寸
(根据您的偏好选择更合适的格式方法):

function getMeta(url){
    $('<img/>',{
        src: url,
        on: {
            load: (e) => {
                console.log('image size:', $(e.target).width(), $(e.target).height());
            },
        }
    });
}

或者

function getMeta(url){
    $('<img/>',{
        src: url,
    }).on({
        load: (e) => {
            console.log('image size:', $(e.target).width(), $(e.target).height());
        },
    });
}

-2

你可以尝试使用包名

import getImageSize from 'image-size-from-url';

const {width, height} = await getImageSize('URL');

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