260 votes

Comment vérifier le type MIME d'un fichier avec javascript avant de le télécharger ?

J'ai lu este y este qui semble suggérer que le type MIME du fichier pourrait être vérifié en utilisant le javascript du côté client. Maintenant, je comprends que la véritable validation doit encore être effectuée côté serveur. Je veux effectuer une vérification côté client pour éviter de gaspiller inutilement les ressources du serveur.

Pour tester si cela peut être fait du côté client, j'ai modifié l'extension d'un fichier de type JPEG fichier de test vers .png et choisissez le fichier à télécharger. Avant d'envoyer le fichier, j'interroge l'objet fichier à l'aide d'une console javascript :

document.getElementsByTagName('input')[0].files[0];

Voici ce que j'obtiens sur Chrome 28.0 :

Fichier {webkitRelativePath : "", lastModifiedDate : Tue Oct 16 2012 10:00:00 GMT+0000 (UTC), nom : "test.png", type : "image/png", taille : 500055 }

Il montre le type à être image/png ce qui semble indiquer que la vérification est effectuée en fonction de l'extension du fichier et non du type MIME. J'ai essayé Firefox 22.0 et il me donne le même résultat. Mais selon la spécification du W3C , Reniflage de MIME doit être mis en œuvre.

Ai-je raison de dire qu'il n'y a aucun moyen de vérifier le type MIME avec javascript pour le moment ? Ou est-ce que quelque chose m'échappe ?

6 votes

I want to perform a client side checking to avoid unnecessary wastage of server resource. Je ne comprends pas pourquoi vous dites que la validation doit être effectuée du côté du serveur, mais dites ensuite que vous voulez réduire les ressources du serveur. Règle d'or : Ne jamais faire confiance aux données de l'utilisateur . Quel est l'intérêt de vérifier le type MIME du côté client si vous le faites ensuite du côté serveur. Il s'agit certainement d'un "gaspillage inutile de ressources". client ressource" ?

10 votes

Fournir une meilleure vérification du type de fichier/un meilleur retour d'information aux utilisateurs côté client est une bonne idée. Cependant, comme vous l'avez indiqué, les navigateurs se basent simplement sur les extensions de fichier pour déterminer la valeur de la propriété type propriété pour File objets. Le code source de webkit, par exemple, révèle cette vérité. Il est possible d'identifier avec précision les fichiers côté client en recherchant, entre autres, des "octets magiques" dans les fichiers. Je travaille actuellement sur une bibliothèque MIT (dans le peu de temps libre dont je dispose) qui fera exactement cela. Si vous êtes intéressé par mes progrès, jetez un coup d'œil à github.com/rnicholus/determinater .

54 votes

@IanClark, le fait est que si le fichier est d'un type non valide, je peux le rejeter du côté client plutôt que de gaspiller la bande passante de téléchargement pour ensuite le rejeter du côté serveur.

4voto

Kailas Points 403

Voici ce que vous devez faire

var fileVariable =document.getElementsById('fileId').files[0];

Si vous voulez vérifier les types de fichiers d'image alors

if(fileVariable.type.match('image.*'))
{
 alert('its an image');
}

1 votes

Ne travaille pas actuellement pour : Firefox pour Android, Opera pour Android, et Safari sur iOS. developer.mozilla.org/fr/US/docs/Web/API/File/type

3voto

lmiguelmh Points 47

Comme le dit Drake, cela pourrait être fait avec FileReader. Cependant, ce que je présente ici est une version fonctionnelle. Prenez en considération que le gros problème de faire cela avec JavaScript est de réinitialiser le fichier d'entrée. Eh bien, cela ne se limite qu'au JPG (pour d'autres formats, vous devrez modifier le paramètre type de mime et le nombre magique ) :

<form id="form-id">
  <input type="file" id="input-id" accept="image/jpeg"/>
</form>

<script type="text/javascript">
    $(function(){
        $("#input-id").on('change', function(event) {
            var file = event.target.files[0];
            if(file.size>=2*1024*1024) {
                alert("JPG images of maximum 2MB");
                $("#form-id").get(0).reset(); //the tricky part is to "empty" the input file here I reset the form.
                return;
            }

            if(!file.type.match('image/jp.*')) {
                alert("only JPG images");
                $("#form-id").get(0).reset(); //the tricky part is to "empty" the input file here I reset the form.
                return;
            }

            var fileReader = new FileReader();
            fileReader.onload = function(e) {
                var int32View = new Uint8Array(e.target.result);
                //verify the magic number
                // for JPG is 0xFF 0xD8 0xFF 0xE0 (see https://en.wikipedia.org/wiki/List_of_file_signatures)
                if(int32View.length>4 && int32View[0]==0xFF && int32View[1]==0xD8 && int32View[2]==0xFF && int32View[3]==0xE0) {
                    alert("ok!");
                } else {
                    alert("only valid JPG images");
                    $("#form-id").get(0).reset(); //the tricky part is to "empty" the input file here I reset the form.
                    return;
                }
            };
            fileReader.readAsArrayBuffer(file);
        });
    });
</script>

Tenez compte du fait que ces tests ont été effectués sur les dernières versions de Firefox et de Chrome, ainsi que sur IExplore 10.

Pour une liste complète des types de mime, voir Wikipédia. .

Pour une liste complète des nombres magiques, voir Wikipedia .

0 votes

Les liens Wikipedia ci-dessus ne sont plus valables.

0 votes

@BobQuinn corrigé, thansk

3voto

Wilter Monteiro Points 76

J'avais besoin de vérifier d'autres types de fichiers.

Suite à la excellente réponse donné par Drakes j'ai trouvé le code ci-dessous après avoir trouvé ce site web avec un tableau très complet des types de fichiers et de leurs en-têtes. En hexadécimal et en chaîne.

J'avais également besoin d'une fonction asynchrone pour gérer de nombreux fichiers et d'autres problèmes liés au projet sur lequel je travaille et qui n'ont pas d'importance ici.

Voici le code en vanilla javascript.

// getFileMimeType
// @param {Object} the file object created by the input[type=file] DOM element.
// @return {Object} a Promise that resolves with the MIME type as argument or undefined
// if no MIME type matches were found.
const getFileMimeType = file => {

    // Making the function async.
    return new Promise(resolve => {
        let fileReader = new FileReader();
        fileReader.onloadend = event => {
            const byteArray = new Uint8Array(event.target.result);

            // Checking if it's JPEG. For JPEG we need to check the first 2 bytes.
            // We can check further if more specific type is needed.
            if(byteArray[0] == 255 && byteArray[1] == 216){
                resolve('image/jpeg');
                return;
            }

            // If it's not JPEG we can check for signature strings directly.
            // This is only the case when the bytes have a readable character.
            const td = new TextDecoder("utf-8");
            const headerString = td.decode(byteArray);

            // Array to be iterated [<string signature>, <MIME type>]
            const mimeTypes = [
                // Images
                ['PNG', 'image/png'],
                // Audio
                ['ID3', 'audio/mpeg'],// MP3
                // Video
                ['ftypmp4', 'video/mp4'],// MP4
                ['ftypisom', 'video/mp4'],// MP4
                // HTML
                ['<!DOCTYPE html>', 'text/html'],
                // PDF
                ['%PDF', 'application/pdf']
                // Add the needed files for your case.
            ];

            // Iterate over the required types.
            for(let i = 0;i < mimeTypes.length;i++){
                // If a type matches we return the MIME type
                if(headerString.indexOf(mimeTypes[i][0]) > -1){
                    resolve(mimeTypes[i][1]);
                    return;
                }
            }

            // If not is found we resolve with a blank argument
            resolve();

        }
        // Slice enough bytes to get readable strings.
        // I chose 32 arbitrarily. Note that some headers are offset by
        // a number of bytes.
        fileReader.readAsArrayBuffer(file.slice(0,32));
    });

};

// The input[type=file] DOM element.
const fileField = document.querySelector('#file-upload');
// Event to detect when the user added files.
fileField.onchange = event => {

    // We iterate over each file and log the file name and it's MIME type.
    // This iteration is asynchronous.
    Array.from(fileField.files, async file => {
        console.log(file.name, await getFileMimeType(file));
    });

};

Notez que dans la fonction getFileMimeType, vous pouvez utiliser deux approches pour trouver le type MIME correct.

  1. Recherchez les octets directement.
  2. Recherche de chaînes de caractères après avoir converti les octets en chaîne de caractères.

J'ai utilisé la première approche avec le JPEG car ce qui le rend identifiable sont les 2 premiers octets et ces octets ne sont pas des caractères lisibles.

Avec le reste des types de fichiers, je pourrais vérifier les signatures de caractères lisibles. Par exemple : [video/mp4] -> 'ftypmp4' ou 'ftypisom'.

Si vous avez besoin de prendre en charge un fichier qui ne figure pas dans la liste de Gary Kessler, vous pouvez consoler.log() les octets ou la chaîne convertie pour trouver une signature appropriée pour le fichier obscur que vous devez prendre en charge.

Note1 : La liste de Gary Kessler a été mise à jour et les signatures mp4 sont différentes maintenant, vous devriez la vérifier lors de l'implémentation. Note2 : le Array.from est conçu pour utiliser une fonction de type .map comme deuxième argument.

1 votes

Vous pouvez rechercher les signatures du format de fichier FourCC ici ; filesignatures.net

2voto

Simon Somlai Points 326

Voici un utilitaire minimal de typecript/promesse pour le navigateur ;

export const getFileHeader = (file: File): Promise<string> => {
  return new Promise(resolve => {
    const headerBytes = file.slice(0, 4); // Read the first 4 bytes of the file
    const fileReader = new FileReader();
    fileReader.onloadend = (e: ProgressEvent<FileReader>) => {
      const arr = new Uint8Array(e?.target?.result as ArrayBufferLike).subarray(
        0,
        4,
      );
      let header = '';
      for (let i = 0; i < arr.length; i++) {
        header += arr[i].toString(16);
      }
      resolve(header);
    };
    fileReader.readAsArrayBuffer(headerBytes);
  });
};

Utilisez comme cela dans votre validation (j'avais besoin d'une vérification PDF) ;

// https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern
const pdfBytePattern = "25504446"
const fileHeader = await getFileHeader(file)
const isPdf = fileHeader === pdfBytePattern // => true

0voto

pathfinder Points 859

Voici une extension de la réponse de Roberto14 qui fait ce qui suit :

CECI N'AUTORISERA QUE LES IMAGES

Vérifie si FileReader est disponible et revient à la vérification de l'extension s'il n'est pas disponible.

Donne une alerte d'erreur si ce n'est pas une image

S'il s'agit d'une image, un aperçu est chargé

** Vous devez toujours effectuer la validation côté serveur, c'est plus une commodité pour l'utilisateur final qu'autre chose. Mais c'est pratique !

<form id="myform">
    <input type="file" id="myimage" onchange="readURL(this)" />
    <img id="preview" src="#" alt="Image Preview" />
</form>

<script>
function readURL(input) {
    if (window.FileReader && window.Blob) {
        if (input.files && input.files[0]) {
            var reader = new FileReader();
            reader.onload = function (e) {
                var img = new Image();
                img.onload = function() {
                    var preview = document.getElementById('preview');
                    preview.src = e.target.result;
                    };
                img.onerror = function() { 
                    alert('error');
                    input.value = '';
                    };
                img.src = e.target.result;
                }
            reader.readAsDataURL(input.files[0]);
            }
        }
    else {
        var ext = input.value.split('.');
        ext = ext[ext.length-1].toLowerCase();      
        var arrayExtensions = ['jpg' , 'jpeg', 'png', 'bmp', 'gif'];
        if (arrayExtensions.lastIndexOf(ext) == -1) {
            alert('error');
            input.value = '';
            }
        else {
            var preview = document.getElementById('preview');
            preview.setAttribute('alt', 'Browser does not support preview.');
            }
        }
    }
</script>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X