0

I'm new to this asynchrony and I'm going crazy. When I finish this, I will try to find out more about it.

My question is, I need to validate on the client side a form. I would like to verify the file if it is really an image, since we can change the extension and pass it by image. I am using this code but of course, the checkFileType function is asynchronous.

In short: I want the file to be validated if it is an image (png, gif or jpg extensions), if it is really an image that displays the image in a preview and / or shows an error.

The code I have is that may be unfinished but it is the following

    $('input[type=file]#imagencabecera').change(function(){
    var file = (this.files[0].name).toString();
    var type = (this.files[0].type).toString();
    var reader = new FileReader();
    console.log(type);

    $('#file-info').text('');
    $('#file-info').text(file);

    reader.onload = function (e){
        $('#filepreview img').attr('src', e.target.result);
    }

    reader.readAsDataURL(this.files[0]);
});

function checkFileType(file){
    if (window.FileReader && window.Blob)
    // All the File APIs are supported. Si soporta HTML5 FileReader y Blob
    {
        var slice = file.slice(0,4);      // Get the first 4 bytes of a file
        var reader = new FileReader();    // Create instance of file reader. It is asynchronous!
        reader.readAsArrayBuffer(slice);  // Read the chunk file and return to blob

        reader.onload = function(e) {
            var buffer = reader.result;          // The result ArrayBuffer
            var view = new DataView(buffer);     // Get access to the result bytes
            var signature = view.getUint32(0, false).toString(16);  // Read 4 bytes, big-endian,return hex string

            switch(signature) // Every file has a unique signature, we can collect them and create a data lib
            {                      
                case "89504e47": file.verified_type = "image/png"; break;
                case "47494638": file.verified_type = "image/gif"; break;
                case "FFd8FFe0": file.verified_type = "image/jpeg"; break;
                case "FFd8FFe1": file.verified_type = "image/jpeg"; break;
                case "FFd8FFe2": file.verified_type = "image/jpeg"; break;                  
                case "FFd8FFe3": file.verified_type = "image/jpeg"; break;
                case "FFd8FFe8": file.verified_type = "image/jpeg"; break;
                default: file.verified_type = 0;
            }
        }

    }
    else
    // File and Blob are not supported
    {

    }
}

Thank you for your attention

MrCodeDev
  • 1
  • 1
  • What specifically are you having trouble with? Your question is a little vague. You might want to look at this question. Might give you an idea http://stackoverflow.com/questions/18299806/how-to-check-file-mime-type-with-javascript-before-upload – James Ross Mar 02 '17 at 16:24
  • I have the problem that I can not get the data type from the switch to the input file. I have no idea how to do it. – MrCodeDev Mar 02 '17 at 18:40

3 Answers3

1

One alternative is to use the method readAsDataURL() from the FileReader object, then you can try to load a image with the result of the FileReader object. If the result is not an image, the error event will be fired.

<html>
<head></head>
<body>
<input type='file' accept='image/*' onchange='openFile(event)'><br>
<img id='output'>
<script>
  var openFile = function(event) {
    var input = event.target;

    var reader = new FileReader();
    reader.onload = function(){
      var dataURL = reader.result;
      var output = document.getElementById('output');
      output.src = dataURL;
   
   output.onerror = function(){
  console.log("error");
   }
   
    };
    reader.readAsDataURL(input.files[0]);
  };
</script>

</body>
</html>
elarmando
  • 569
  • 1
  • 7
  • 16
  • That method would not work well, since I would be watching the extension. If I for example change an mp3 and I put extension jpg would pass the test. If I do it for the proposed form no. The problem is that the code I have put is missing the check and pass the error. But asynchronous I do not know how to do it – MrCodeDev Mar 02 '17 at 22:39
  • You're right, i edited the previous answer for a better solution. – elarmando Mar 02 '17 at 23:07
  • You must also declare the reader.onload callback before the invocation of readAsArrayBuffer method – elarmando Mar 02 '17 at 23:26
0

You can do it using the "onchange" event, try something like this:

$(document).ready(function() {
  
  $('.myinput').bind('change', function() {
   if ($(this).val().split('.').pop() !== "jpg") {
    $('.error-message').text('Not a JPG');
    $(this).val(null);
   }
   else {
   $('.error-message').text('');
   }
  })
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
<input type="file" class="myinput">
<span class="error-message"></span>
</form>
  • This would not work with fake files with the changed extension. That's why I need to do it with the header. – MrCodeDev Mar 02 '17 at 18:42
  • Then this might help: http://stackoverflow.com/questions/32912803/client-side-image-resolution-size-validation – Daniel Jimenez Mar 02 '17 at 19:42
  • At the moment that motodo would work to see the width of an image but not for the cabercera mime / type. Anyway thanks – MrCodeDev Mar 02 '17 at 22:42
0

You have two options:

  • Promises
  • Callbacks

Promises (ES6)

The promises were introduced in (aka ES6) and are a solution proposed by ECMAScript to the famous callback hell. The promises are called a function with two parameters:

  • resolve
  • reject

The 'resolve' function is used to indicate that a data is to be delivered by whoever expects the promise. The 'reject' is used to indicate that something in the process has failed.

let chooser = document.getElementById('chooser');

chooser.addEventListener('change', checkMime);

function getRealMime(file) {
  return new Promise((resolve, reject) => {
    if (window.FileReader && window.Blob) {
      let slice = file.slice(0, 4);
      let reader = new FileReader();
      
      reader.onload = () => {
        let buffer = reader.result;
        let view = new DataView(buffer);
        let signature = view.getUint32(0, false).toString(16);
        let mime = 'unknown';

        switch (signature) {
          case "89504e47":
            mime = "image/png";
            break;
          case "47494638":
            mime = "image/gif";
            break;
          case "FFd8FFe0":
            mime = "image/jpeg";
            break;
          case "FFd8FFe1":
            mime = "image/jpeg";
            break;
          case "FFd8FFe2":
            mime = "image/jpeg";
            break;
          case "FFd8FFe3":
            mime = "image/jpeg";
            break;
          case "FFd8FFe8":
            mime = "image/jpeg";
            break;
        }

        resolve(mime);
      }
      
      reader.readAsArrayBuffer(slice);
    } else {
      reject(new Error('Usa un navegador moderno para una mejor experiencia'));
    }
  });
}

function checkMime(e) {
  getRealMime(this.files[0])
    .then(mime => {
      console.info(`Real mime: ${mime}`);
      // evaluar si el mime está disponible
      
    })
    .catch(err => {
      console.error(err.message);
    });
}
input[type="file"] {
  display: none;
}

label {
  background-color: #ff0065;
  border-radius: 3px;
  color: #fff;
  cursor: pointer;
  display: inline-block;
  font-family: 'Segoe UI', 'Ubuntu', sans-serif;
  font-size: 14px;
  height: 35px;
  line-height: 35px;
  padding: 0 12px;
}
<input type="file" id="chooser"/>
<label for="chooser">Select file</label>

Callbacks (ES5)

This form is used de facto in ES5. It consists of passing a function as parameter to be executed once the main function has finished its process.

var chooser = document.getElementById('chooser');

chooser.addEventListener('change', checkMime);


function getRealMime(file, cb) {
  if (window.FileReader && window.Blob) {
    var slice = file.slice(0, 4);
    var reader = new FileReader();
    reader.onload = () => {
      var buffer = reader.result;
      var view = new DataView(buffer);
      var signature = view.getUint32(0, false).toString(16);
      var mime = 'unknown';
        
      switch(signature) {                      
        case "89504e47": mime = "image/png"; break;
        case "47494638": mime = "image/gif"; break;
        case "FFd8FFe0": mime = "image/jpeg"; break;
        case "FFd8FFe1": mime = "image/jpeg"; break;
        case "FFd8FFe2": mime = "image/jpeg"; break;                  
        case "FFd8FFe3": mime = "image/jpeg"; break;
        case "FFd8FFe8": mime = "image/jpeg"; break;
      }

      cb(null, mime);
    }
    
    reader.readAsArrayBuffer(slice);
  } else {
    cb(new Error('Usa un navegador moderno para una mejor experiencia'), null);
  }
}

function checkMime (e) {
  getRealMime(this.files[0], function (err, mime) {
    if (err) {
      console.error(err.message);
    } else {
      console.info('Real mime', mime);
    }
  });
}
input[type="file"] {
  display: none;
}

label {
  background-color: #ff0065;
  border-radius: 3px;
  color: #fff;
  cursor: pointer;
  display: inline-block;
  font-family: 'Segoe UI', 'Ubuntu', sans-serif;
  font-size: 14px;
  height: 35px;
  line-height: 35px;
  padding: 0 12px;
}
<input type="file" id="chooser"/>
<label for="chooser">Select file</label>

This answer is from Gus (https://es.stackoverflow.com/users/26302/guz) by StackOverflow in Spanish.

Community
  • 1
  • 1
MrCodeDev
  • 1
  • 1