使用JavaScript检查服务器上的图像是否存在?

163

使用JavaScript有没有一种方法可以判断服务器上的资源是否可用?例如,我已经将图像1.jpg-5.jpg加载到html页面中。我想每隔一分钟调用一个JavaScript函数,大致执行以下伪代码...

if "../imgs/6.jpg" exists:
    var nImg = document.createElement("img6");
    nImg.src = "../imgs/6.jpg";

你有什么想法?谢谢!

18个回答

257
您可以使用类似以下的内容:

您可以使用以下内容:

function imageExists(image_url){

    var http = new XMLHttpRequest();

    http.open('HEAD', image_url, false);
    http.send();

    return http.status != 404;

}

显然,您可以使用jQuery或类似的工具来执行HTTP请求。
$.get(image_url)
    .done(function() { 
        // Do something now you know the image exists.

    }).fail(function() { 
        // Image doesn't exist - do something else.

    })

6
函数名称不应该是fileExists吗?因为它适用于.js、.css、.html或任何其他公开可用的文件。 - CoR
1
函数太棒了。我把它放在我的藏品里 :) 我认为 fileExists 会是更好的名字,因为这个函数并不检查图像是否存在于服务器上。它只检查文件是否可以从服务器访问。没有检查该文件是否实际上是一个图像。它可能是.pdf、.html、一些随机文件重命名为*.jpg或*.png。如果某事以 .jpg 结尾,这并不意味着它是100% 的图像 :) - CoR
11
除非符合CORS规则允许访问资源,否则此操作将失败。使用“Image”对象不会受到此限制。其唯一的优点是它实际上不会下载图像。 - Alnitak
8
跨域问题。 - Amit Kumar
2
这确实可以工作,但请记住它需要更长的时间来处理请求,而且不是最好的解决方案。此外,它在跨域上不起作用(在Chrome中肯定如此),因此您无法在file:///协议上使用它,这意味着无法在本地使用。 - Cameron
显示剩余9条评论

157

您可以利用图像预加载器的基本工作方式来测试图像是否存在。

    function checkImage(imageSrc, good, bad) {
        var img = new Image();
        img.onload = good; 
        img.onerror = bad;
        img.src = imageSrc;
    }
    
    checkImage("foo.gif", function(){ console.log("good"); }, function(){ console.log("bad"); } );

    checkImage("favicon.ico", function(){ console.log("good"); }, function(){ console.log("bad"); } );

JSFiddle

{{链接1:JSFiddle}}


嘿,很酷的函数!顺便说一下,这是一种有趣的返回结果的方式,直接进入匿名函数。我通常会像@ajtrichards在他的答案的第一部分中所做的那样使用return语句来完成这个操作。 - Sablefoste
3
对于未来遇到这个问题的谷歌搜索者,尝试将img.src定义移动到新Image行之后。 - Gavin
1
如果图像服务器返回实际图像内容的404错误,例如Youtube缩略图服务所做的那样,这将无法帮助捕获404错误:https://i3.ytimg.com/vi/vGc4mg5pul4/maxresdefault.jpg - gvlasov
头部请求由于CORS无法工作。我想唯一的解决方案是使用YouTube API。 - Ilya Levin
我不得不对这段代码进行相当大的修改才能使它与我的程序配合使用,但是它非常聪明,干得好! - Dennis Kozevnikoff
显示剩余2条评论

69
您可以使用提供给所有图像的内置事件来检查图像是否加载。 onloadonerror事件将告诉您图像是否成功加载或是否发生错误:

var image = new Image();

image.onload = function() {
    // image exists and is loaded
    document.body.appendChild(image);
}
image.onerror = function() {
    // image did not load

    var err = new Image();
    err.src = '/favicon.ico';

    document.body.appendChild(err);
}

image.src = "../imgs/6.jpg";


2
对我来说最好的答案,因为它适用于所有情况(连接问题、外部服务器...)+1 - Reign.85
3
与使用AJAX.get的替代方法相比,这个答案的性能要快得多! - Samuel
我喜欢这个解决方案,但是它在两种情况下都引入了事件(异步执行),这可能会在某些情况下引起问题:我建议无论如何都将图像附加,然后仅在出现错误的情况下进行替换。 - Giorgio Tempesta
@adeno,当状态码为404 Not Found时,它能正常工作吗? - Mahi
@Mahi - 状态码并不重要,只要404页面没有返回有效的图像,它就会失败并触发“error”处理程序。 - adeneo

29

更好、现代的方法是使用ES6 Fetch API 来检查图片是否存在:

fetch('https://via.placeholder.com/150', { method: 'HEAD' })
    .then(res => {
        if (res.ok) {
            console.log('Image exists.');
        } else {
            console.log('Image does not exist.');
        }
    }).catch(err => console.log('Error:', err));

请确保您正在进行相同源请求,或者服务器已启用CORS。


2020年最简单的方法。谢谢。 - Nadav
这太不可思议了。这是什么时候出现的? - Kyle Baker
@KyleBaker Fetch API 已经存在很长时间了。现在几乎所有现代浏览器都广泛支持它 - 几乎 ~95%。 - attacomsian

16
如果有人来到这个页面想要在基于React的客户端中执行此操作,您可以像下面这样做,这是由React团队的Sophia Alpert在这里提供的答案。请注意保留HTML标签。
getInitialState: function(event) {
    return {image: "http://example.com/primary_image.jpg"};
},
handleError: function(event) {
    this.setState({image: "http://example.com/failover_image.jpg"});
},
render: function() {
    return (
        <img onError={this.handleError} src={src} />;
    );
}

15

基本上就是@espascarello和@adeneo回答的一个经过承诺化的版本,带有一个回退参数:

const getImageOrFallback = (path, fallback) => {
  return new Promise(resolve => {
    const img = new Image();
    img.src = path;
    img.onload = () => resolve(path);
    img.onerror = () => resolve(fallback);
  });
};

// Usage:

const link = getImageOrFallback(
  'https://www.fillmurray.com/640/360',
  'https://via.placeholder.com/150'
  ).then(result => console.log(result) || result)

2021年3月更新: 在与@hitautodestruct的交谈后,我决定添加一个更具“规范性”的Promise函数版本。现在拒绝了onerror情况,但随后被捕获并返回默认值:

function getImageOrFallback(url, fallback) {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.src = url
    img.onload = () => resolve(url)
    img.onerror = () => {
      reject(`image not found for url ${url}`)
    }
  }).catch(() => {
    return fallback
  })
}

getImageOrFallback("https://google.com", "https://picsum.photos/400/300").then(validUrl => {
  console.log(validUrl)
  document.body.style.backgroundImage = `url(${validUrl})`
})
html, body {
  height: 100%;
  background-repeat: no-repeat;
  background-position: 50% 50%;
  overflow-y: hidden;
}

注意: 我个人可能更喜欢fetch的解决方案,但它有一个缺点 - 如果您的服务器以特定方式配置,则即使文件不存在,它也可以返回200 / 304。另一方面,这种方法可以完成工作。


2
对于 fetch,您可以使用 response 对象的 ok 属性来检查请求是否成功:fetch(url).then(res => {if(res.ok){ /*存在*/} else {/*不存在*/}}); - attacomsian
1
如果我想检查有效的背景图像,那么这方法对我来说不起作用。background-image: url('/a/broken/url') 在 Chrome 74 中会得到 200ok 的返回。 - HynekS
@HynekS 我建议在 onerror 回调中拒绝 Promise,而不是解决它。new Promise( (resolve, reject) => { /*...*/ img.onerror = () => reject('Not found') } ) - hitautodestruct
@hitautodestruct,即使在“错误”情况下,我总是进行解析的原因是我经常在Promise.all()内使用此模式,其中拒绝一个项目将拒绝整个列表。而且,我期望它总是返回某些内容(即现有的图像URL:因此函数名称)。虽然我不是FP极客,但我喜欢将其视为一个Either单子。 - HynekS
@HynekS 你可能想注意答案中使用了 Promise.all()。 我认为,如果你的答案是“一个经过承诺的版本...”,那么它应该尽可能接近经典的承诺,否则可能会对不熟悉承诺的人造成困惑。此外,您可能要考虑使用 Promise.allSettled() 而不是 Promise.all(),以便在任何 reject 上不会失败所有承诺。 - hitautodestruct
@hitautodestruct 理解了,我添加了一个更“规范”的函数版本(在onerror上拒绝,但然后捕获并返回默认值)。它应该也能在Promise.all中正常工作。感谢David Walsh(https://davidwalsh.name/javascript-promise-catch)的贡献。 - HynekS

6
如果您创建一个图像标签并将其添加到DOM中,那么它的onload或onerror事件都应该被触发。如果onerror被触发,则表示服务器上不存在该图像。

5
您可以调用此JS函数来检查服务器上的文件是否存在:
function doesFileExist(urlToFile)
{
    var xhr = new XMLHttpRequest();
    xhr.open('HEAD', urlToFile, false);
    xhr.send();

    if (xhr.status == "404") {
        console.log("File doesn't exist");
        return false;
    } else {
        console.log("File exists");
        return true;
    }
}

1
不错的努力。但是在单个表单中加载多个图像时会有点耗时。 - Nuwan Withanage
如果URL不存在,则在xhr.send()处显示错误。 - Avinash Sharma

3

这里提供了基于true/false返回值的Promise函数版本:

使用new Image();方法(推荐):

async function imageExists(imgUrl) {
    if (!imgUrl) {
        return false;
    }
    return new Promise(res => {
        const image = new Image();
        image.onload = () => res(true);
        image.onerror = () => res(false);
        image.src = imgUrl;
    });
}

// test
(async () => {
  // true
  console.log(await imageExists('https://www.gstatic.com/webp/gallery3/1.png'));
  // false
  console.log(await imageExists('https://www.gstatic.com/webp/gallery3/DOES_NOT_EXIST_THERE.png'));
})()

使用XMLHttpRequest(数据较少,但可能会导致跨域问题):

async function fileExists(fileUrl) {
    if (!fileUrl) {
        return false;
    }
    return new Promise(res => {
        const http = new XMLHttpRequest();
        http.open('HEAD', fileUrl, true);
        http.send();
        http.onload = () => {
            res(http.status != 404);
        };
        http.onerror = () => {
            res(false);
        };
    });
}

// test
(async () => {
  // true
  console.log(await fileExists('https://www.gstatic.com/webp/gallery3/1.png'));
  // false
  console.log(await fileExists('https://www.gstatic.com/webp/gallery3/DOES_NOT_EXIST_THERE.png'));
})()

使用fetch(现代浏览器,数据更少,可能存在CORS问题)

async function fileExists(fileUrl) {
    if (!fileUrl) {
        return false;
    }
    const res = await fetch(fileUrl, {
      method: 'HEAD',
    });
    return !!res.ok;
}

// test
(async () => {
  // false
  console.log(await fileExists('https://www.gstatic.com/webp/gallery3/1.png'));
  // false
  console.log(await fileExists('https://www.gstatic.com/webp/gallery3/DOES_NOT_EXIST_THERE.png'));
  

})()

TypeScript 版本:
// with `new Image();` method
async function imageExists(imgUrl: string | null | undefined): Promise<boolean> {
  if (!imgUrl) {
    return false;
  }

  return new Promise(res => {
    const image = new Image();
    image.onload = () => res(true);
    image.onerror = () => res(false);
    image.src = imgUrl;
  });
}

// with `XMLHttpRequest` method
async function fileExists(fileUrl: string | null | undefined): Promise<boolean> {
  if (!fileUrl) {
    return false;
  }

  return new Promise(res => {
    const http = new XMLHttpRequest();

    http.open('HEAD', fileUrl, false);
    http.send();
    http.onload = () => {
        res(http.status != 404);
    };
    http.onerror = () => {
        res(false);
    };

  });
}

// with `fetch` method
async function fileExists(fileUrl: string | null | undefined): Promise<boolean>  {
  if (!fileUrl) {
    return false;
  }
  const res = await fetch(fileUrl, {
    method: 'HEAD',
  });

  return !!res.ok;
}

3

我在使用JS显示用户图像时遇到了一些问题:如果用户图像丢失,它会报错。

事实证明,您可以在HTML中执行以下操作,以在加载图像失败时显示特定图像

<img src="img/path/image.png" onerror="javascript:this.src='img/path/no-image.png'">

只是想分享一下...也许这可以为你节省一些时间!


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