espresso_coffee
espresso_coffee

Reputation: 6110

How to check if image exists?

I have application where images should be set in some of the pages. These images are on the server and each image has unique ID appended. Here is example of the image names:

AA_image1.jpg
AB_image1.jpg
AC_image1.jpg
AD_image1.jpg
AE_image1.jpg

I seen what previous developers did and how they checked the image existance. They used JSON file that has id and image name. Here is example of that code:

var images = [{
    "id": "AA",
    "image": "AA_image1.jpg"
  },
  {
    "id": "AB",
    "image": "AB_image1.jpg"
  },
  {
    "id": "AC",
    "image": "AC_image1.jpg"
  },
  {
    "id": "AD",
    "image": "AD_image1.jpg"
  },
  {
    "id": "AE",
    "image": "AE_image1.jpg"
  }
];

var imgID = "AC";
var imgPrimary = "AC_image1.jpg";
var found = false;
var imgDefault = "default.jpg";

for (i = 1; i < images.length; i++) {
  if (images[i].id == imgID && (images[i].image).toLowerCase() == (imgID + '_image1.jpg').toLowerCase()) {
    found = true;
    break;
  }
}

if (found === true) {
  console.log(imgPrimary);
} else {
  console.log(imgDefault);
}

The example above seems pretty simple but my concern is what if image get removed from the folder and JSON file is not updated? In that case we would load the image that do not exist instead of default image. I'm wondering if this approach would be better:

var imgID = "AC";
var imgNames = [imgID + '_image1','default'];
var imgResults = {};

for(var i = 0;  i < imgNames.length; i++){
   checkImage( imgNames[i] );
}

function checkImage( imgName, keyName ) {
    $.ajax({
        type: "GET",
        async: true,
        url: "images/"+imgName+".jpg",
    }).done(function(message,text,jqXHR){
         imgResults[imgName] = true;
    }).fail(function(jqXHR, textStatus, errorThrown){
         imgResults[imgName] = false;
    });
 }

Here is example of imgResults after the process was completed:

console.log(imgResults);

Console result:

{
  "default": true,
  "AG_image1": false
}

The only problem I'm experiencing with the second example is that if I try to check the result based on the key I'm getting undefined. Here is example:

console.log(imgResults["AG_image1"]);

This is result in the console:

undefined

Between the two I'm not sure which one is better and more secure. If anyone have suggestions please let me know.

Upvotes: 1

Views: 1324

Answers (2)

Teemu
Teemu

Reputation: 23416

This is probably the shortest possible code to handle lost images in JS. imagePad is a reference to the DOM element in which you want to show the image.

var img = new Image();
img.addEventListener('error', function (e) {
    // Image not found, show the default
    this.src = iconBase + 'gen.png';
});
img.addEventListener('load', function () {
    imagePad.appendChild(img);
}
img.src = 'the_given_src';

Upvotes: 2

user4639281
user4639281

Reputation:

AJAX request are asynchronous by default

The reason that you're unable to search the imgResults object when you are trying is because AJAX requests are asynchronous, and the request has not completed when you're trying to access the results. You need to wait for the request to complete before continuing.

The reason that console.log shows the results is because console.log is lazy and doesn't evaluate the object until you expand it in dev tools, console.dir will show you an empty object at that point.

Furthermore, as you have multiple requests that you want to wait for, you'll want to create an array of promises where each promise corresponds to the load/failure of each request, then use Promise.all to wait for all the promises to complete before operating on the results.

Verifying that a resource exists

To verify that a resource exists, you can use a HEAD request instead of a GET request, such as to prevent loading the entire image for no reason. There's also no need to depend on a massive library such as jQuery for AJAX requests as XMLHttpRequest is very well supported in this day and age.

class ResourceValidator extends EventTarget {
  constructor(target) {
    super()
    this.target = target
    this._ready = false
    this._valid = false
    this.validate()
  }
  validate() {
    const request = new XMLHttpRequest()
    request.addEventListener('load', event => {
        this._ready = true
        this._valid = true
        this.dispatchEvent(new Event('ready'))
    })
    request.addEventListener('error', event => {
        this._ready = true
        this.dispatchEvent(new Event('ready'))
    })
    request.open('HEAD', this.target)
    request.send()
  }
  get ready() {
    return new Promise(resolve => {
      if(this._ready === true) resolve(true)
      else this.addEventListener('ready', _ => resolve(true))
    })
  }
  get valid() {
    return new Promise(resolve => {
      if(this._ready === true) resolve(this._valid)
      else this.addEventListener('ready', _ => resolve(this._valid))
    })
  }
}

async function validateImageSources(sources) {
  const results = {}
  const promises = []

  for(let source of sources) {
     const validator = new ResourceValidator(source)
     const promise = validator.valid
     promise.then(valid => results[source] = valid)
     promises.push(promise)
  }
  
  await Promise.all(promises)
  
  return results
}

validateImageSources([
  'https://picsum.photos/200',
  'https://nosuchaddress.io/image.png'
]).then(results => {
  console.log(results)
  console.log(results['https://picsum.photos/200'])
})

Upvotes: 1

Related Questions