Checking If A File Exists

by kirupa | 8 November 2012

From images to stylesheets to script files, what you see in your HTML document often involves a collection of files that work together to display the final result:


This is a companion discussion topic for the original entry at http://www.kirupa.com/html5/checking_if_a_file_exists.htm

Hi Kirupa!

I just noticed that sending an XMLHttpRequest with a “false” flag is now deprecated and is throwing lots of errors in my code. If I leave it out or set it to “true” the browser engine goes into an endless loop till it crashes.

Is there another way to check the existence of a file on the server without loading it?

Generally you want to avoid all blocking APIs in UI, such as in client-side browser code. This reference show examples of how to handle async XMLHttpRequest requests (when the false is true):

Thanks senocular, but that doesn’t really answer my question.

With some assistance from his lordship, the Rt Hon Kirupa, I built a function which iterates through a loop building an array of image names, without loading the actual images, by checking that they exist in the folder on the server. When it finds a 404 it breaks out of the loop and writes the matrix for the thumbnails to display in.

It uses the XMLHttpRequest to check the HEAD of the file on the server on each loop. This only works this way if the request is handled synchronously.

At this stage the function works perfectly and allows me to add images easily to the matrix without writing any new code. I just name them appropriately and stick them in the folder.

But if running the XMLHttpRequest synchronously in the main js thread is at some point going to stop working, I need to find another way to achieve the same result.

So far I have managed to get the site to do everything I want to using pure CSS3 and HTML5 with plain js. Which is pretty cool. No flash, no jquery or other plugins.

(still to build the mobile version)

The answer is in the link using event listeners/callbacks to handle the requests asynchronously instead of synchronously. If you want to have each image checked as a precondition for checking the next, it will require reworking the loop to instead recursively check in the complete handlers.

https://jsfiddle.net/8tzxpr63/

Thanks senocular! When I get a chance I will take a look and see how I can improve my code based on your fiddle. I have managed to get it error free apart from this across all the PC browsers! Hurrah!

Since @senocular picks on me for using xhr instead of Fetch, I figured I’d post a solution that uses the newer Fetch API instead :stuck_out_tongue:

fetch("https://www.kirupa.com/book/images/learning_react2.gif", {
    method: "head",
    mode: "no-cors"
})
.then(function(response) {
    if (response.status == 200) {
        console.log("file exists!");
    } else {
        console.log("file doesn't exist!");
    }
    return response.text();
})
.catch(function(error) {
  console.log("Error ", error);
});

The result should be nearly identical!

Cheers,
Kirupa

Now write a version that loops over a list of images but stops loading any remaining image if one isn’t found :grinning:

(I dont know why I made my example do a type check :stuck_out_tongue_closed_eyes: )

1 Like

No takers? I’ll put one up, this one using reduce:

var files = [
  "https://www.kirupa.com/book/images/learning_react.gif",
  "https://www.kirupa.com/book/images/learning_react2.gif"
];

files.reduce((promise, file) => 
  promise.then(() => fetch(file, {
    method: "head",
    mode: "no-cors"
  }))
  .then(response => {
    if (response.status !== 200) {
      throw "file doesn't exist!";
    } else {
      console.log("file exists!");
    }
  }), Promise.resolve())
.catch(function(error) {
  console.log("Error ", error);
});

And if you want to get all cutting edge about it, here’s one with async iteration:

async function* fetchImages (imgs) {
  while (imgs.length) {
	  yield await fetch(imgs.shift(), {
    	method: "head",
    	mode: "no-cors"
    });
  }
}

async function checkImages (imgs) {
  try {

    for await (const response of fetchImages(imgs)) {
      if (response.status === 200) {
      	console.log("file exists!");
      } else {
        throw "file doesn't exist!";
      }
    }

  } catch (error) {
    console.log("Error ", error);
  }
}

checkImages([
  "https://www.kirupa.com/book/images/learning_react.gif",
  "https://www.kirupa.com/book/images/learning_react2.gif"
]);

I’m loving this one-upmanship! Very illuminating! Haven’t had the chance to change my page code yet - seems like a good thing! Now its up to you Kirupa to achieve the same thing with half as much code.

Looking forward to going through this and making use of it!

Thanks guys!

1 Like

One thing that all of the solutions posted so far run into is CORS issues when trying to check for content on other domains/hosts than your own. Here is one approach using a CORS bypass proxy that solves that problem:

let CORS_override = "https://cors-anywhere.herokuapp.com/";

function doesFileExist(pathToFile) {
  //
  // Remove CORS_override from the 'fetch' call if you'll 
  // only be checking for files on your own domain
  //
  fetch(CORS_override + pathToFile)
    .then(function (response) {
      if (response.status != "404") {
        console.log(pathToFile + " exists: " + response.statusText);
      } else {
        console.log(pathToFile + " does not exist: " + response.statusText);
      }
    })
    .catch(function (error) {
      console.log("Probably a network error!");
    });
}

doesFileExist("https://www.kirupa.com/ssi/newDesign/kirupaLogo_large2.png");
doesFileExist("https://www.kirupa.com/ssi/newDesign/kirupaLogo_large.png");