wake-up-neo.com

Wie kann ich den MIME-Dateityp vor dem Hochladen mit Javascript überprüfen?

Ich habe this und this Fragen gelesen, die darauf hindeuten, dass der MIME-Dateityp clientseitig mit Javascript überprüft werden kann. Jetzt verstehe ich, dass die eigentliche Validierung noch serverseitig erfolgen muss. Ich möchte eine clientseitige Überprüfung durchführen, um unnötige Verschwendung von Serverressourcen zu vermeiden.

Um zu testen, ob dies auf Client-Seite möglich ist, habe ich die Erweiterung einer JPEG-Testdatei in .png Geändert und die Datei zum Hochladen ausgewählt. Vor dem Senden der Datei frage ich das Dateiobjekt mit einer Javascript-Konsole ab:

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

Das bekomme ich auf Chrome 28.0:

Datei {webkitRelativePath: "", lastModifiedDate: Di Okt 16 2012 10:00:00 GMT + 0000 (UTC), Name: "test.png", Typ: "image/png", Größe: 500055…}

Es zeigt an, dass der Typ image/png Ist, was darauf hinzudeuten scheint, dass die Prüfung auf der Basis der Dateierweiterung anstelle des MIME-Typs erfolgt. Ich habe versucht, Firefox 22.0 und es gibt mir das gleiche Ergebnis. Aber gemäß der W3C-Spezifikation sollte MIME-Sniffing implementiert werden.

Kann ich zu Recht sagen, dass es derzeit keine Möglichkeit gibt, den MIME-Typ mit Javascript zu überprüfen? Oder vermisse ich etwas?

141

Sie können den MIME-Dateityp einfach mit dem JavaScript-Befehl FileReader ermitteln, bevor Sie ihn auf einen Server hochladen. Ich stimme zu, dass wir die serverseitige Überprüfung der clientseitigen vorziehen sollten, die clientseitige Überprüfung jedoch weiterhin möglich ist. Ich zeige Ihnen, wie es geht und stelle unten eine funktionierende Demo zur Verfügung.


Stellen Sie sicher, dass Ihr Browser sowohl File als auch Blob unterstützt. Alle wichtigen sollten.

if (window.FileReader && window.Blob) {
    // All the File APIs are supported.
} else {
    // File and Blob are not supported
}

Schritt 1:

Sie können die File -Information von einem <input> - Element wie folgt abrufen ( ref ):

<input type="file" id="your-files" multiple>
<script>
var control = document.getElementById("your-files");
control.addEventListener("change", function(event) {
    // When the control has changed, there are new files
    var files = control.files,
    for (var i = 0; i < files.length; i++) {
        console.log("Filename: " + files[i].name);
        console.log("Type: " + files[i].type);
        console.log("Size: " + files[i].size + " bytes");
    }
}, false);
</script>

Hier ist eine Drag-and-Drop-Version des oben genannten ( ref ):

<div id="your-files"></div>
<script>
var target = document.getElementById("your-files");
target.addEventListener("dragover", function(event) {
    event.preventDefault();
}, false);

target.addEventListener("drop", function(event) {
    // Cancel default actions
    event.preventDefault();
    var files = event.dataTransfer.files,
    for (var i = 0; i < files.length; i++) {
        console.log("Filename: " + files[i].name);
        console.log("Type: " + files[i].type);
        console.log("Size: " + files[i].size + " bytes");
    }
}, false);
</script>

Schritt 2:

Wir können jetzt die Dateien untersuchen und Header und MIME-Typen herausfiltern.

✘ Schnelle Methode

Sie können naiv Blob nach dem MIME-Typ der Datei fragen, für die dieses Muster verwendet wird:

var blob = files[i]; // See step 1 above
console.log(blob.type);

Für Bilder werden MIME-Typen wie folgt zurückgegeben:

image/jpeg
image/png
...

Einschränkung: Der MIME-Typ wird von der Dateierweiterung erkannt und kann getäuscht oder gefälscht werden. Man kann einen .jpg In einen .png Umbenennen und der MIME-Typ wird als image/png Gemeldet.


✓ Richtige Methode zur Überprüfung des Headers

Um den bonafide MIME-Typ einer clientseitigen Datei zu erhalten, können wir einen Schritt weiter gehen und die ersten Bytes der angegebenen Datei mit den sogenannten magischen Zahlen vergleichen. Seien Sie gewarnt, dass dies nicht ganz einfach ist, da beispielsweise JPEG ein paar "magische Zahlen" enthält. Dies liegt daran, dass sich das Format seit 1991 weiterentwickelt hat. Möglicherweise müssen Sie nicht nur die ersten beiden Bytes überprüfen, sondern ich bevorzuge die Überprüfung von mindestens 4 Bytes, um False Positives zu reduzieren.

Beispieldateisignaturen von JPEG (erste 4 Bytes):

FF D8 FF E0 (SOI + ADD0)
FF D8 FF E1 (SOI + ADD1)
FF D8 FF E2 (SOI + ADD2)

Hier ist der wesentliche Code zum Abrufen des Datei-Headers:

var blob = files[i]; // See step 1 above
var fileReader = new FileReader();
fileReader.onloadend = function(e) {
  var arr = (new Uint8Array(e.target.result)).subarray(0, 4);
  var header = "";
  for(var i = 0; i < arr.length; i++) {
     header += arr[i].toString(16);
  }
  console.log(header);

  // Check the file signature against known types

};
fileReader.readAsArrayBuffer(blob);

Sie können dann den tatsächlichen MIME-Typ wie folgt bestimmen (weitere Dateisignaturen hier und hier ):

switch (header) {
    case "89504e47":
        type = "image/png";
        break;
    case "47494638":
        type = "image/gif";
        break;
    case "ffd8ffe0":
    case "ffd8ffe1":
    case "ffd8ffe2":
    case "ffd8ffe3":
    case "ffd8ffe8":
        type = "image/jpeg";
        break;
    default:
        type = "unknown"; // Or you can use the blob.type as fallback
        break;
}

Akzeptieren oder lehnen Sie Datei-Uploads nach Belieben ab, basierend auf den erwarteten MIME-Typen.


Demo

Hier ist eine funktionierende Demo für lokale Dateien und entfernte Dateien (ich musste CORS nur für diese Demo umgehen). Öffnen Sie das Snippet, führen Sie es aus, und Sie sollten drei Remote-Images verschiedener Typen anzeigen. Oben können Sie eine lokale Bilddatei auswählen oder, und die Dateisignatur und/oder der MIME-Typ werden angezeigt.

Beachten Sie, dass selbst wenn ein Bild umbenannt wird, der wahre MIME-Typ bestimmt werden kann. Siehe unten.

Screenshot

Expected output of demo


// Return the first few bytes of the file as a hex string
function getBLOBFileHeader(url, blob, callback) {
  var fileReader = new FileReader();
  fileReader.onloadend = function(e) {
    var arr = (new Uint8Array(e.target.result)).subarray(0, 4);
    var header = "";
    for (var i = 0; i < arr.length; i++) {
      header += arr[i].toString(16);
    }
    callback(url, header);
  };
  fileReader.readAsArrayBuffer(blob);
}

function getRemoteFileHeader(url, callback) {
  var xhr = new XMLHttpRequest();
  // Bypass CORS for this demo - naughty, Drakes
  xhr.open('GET', '//cors-anywhere.herokuapp.com/' + url);
  xhr.responseType = "blob";
  xhr.onload = function() {
    callback(url, xhr.response);
  };
  xhr.onerror = function() {
    alert('A network error occurred!');
  };
  xhr.send();
}

function headerCallback(url, headerString) {
  printHeaderInfo(url, headerString);
}

function remoteCallback(url, blob) {
  printImage(blob);
  getBLOBFileHeader(url, blob, headerCallback);
}

function printImage(blob) {
  // Add this image to the document body for proof of GET success
  var fr = new FileReader();
  fr.onloadend = function() {
    $("hr").after($("<img>").attr("src", fr.result))
      .after($("<div>").text("Blob MIME type: " + blob.type));
  };
  fr.readAsDataURL(blob);
}

// Add more from http://en.wikipedia.org/wiki/List_of_file_signatures
function mimeType(headerString) {
  switch (headerString) {
    case "89504e47":
      type = "image/png";
      break;
    case "47494638":
      type = "image/gif";
      break;
    case "ffd8ffe0":
    case "ffd8ffe1":
    case "ffd8ffe2":
      type = "image/jpeg";
      break;
    default:
      type = "unknown";
      break;
  }
  return type;
}

function printHeaderInfo(url, headerString) {
  $("hr").after($("<div>").text("Real MIME type: " + mimeType(headerString)))
    .after($("<div>").text("File header: 0x" + headerString))
    .after($("<div>").text(url));
}

/* Demo driver code */

var imageURLsArray = ["http://media2.giphy.com/media/8KrhxtEsrdhD2/giphy.gif", "http://upload.wikimedia.org/wikipedia/commons/e/e9/Felis_silvestris_silvestris_small_gradual_decrease_of_quality.png", "http://static.giantbomb.com/uploads/scale_small/0/316/520157-Apple_logo_dec07.jpg"];

// Check for FileReader support
if (window.FileReader && window.Blob) {
  // Load all the remote images from the urls array
  for (var i = 0; i < imageURLsArray.length; i++) {
    getRemoteFileHeader(imageURLsArray[i], remoteCallback);
  }

  /* Handle local files */
  $("input").on('change', function(event) {
    var file = event.target.files[0];
    if (file.size >= 2 * 1024 * 1024) {
      alert("File size must be at most 2MB");
      return;
    }
    remoteCallback(escape(file.name), file);
  });

} else {
  // File and Blob are not supported
  $("hr").after( $("<div>").text("It seems your browser doesn't support FileReader") );
} /* Drakes, 2015 */
img {
  max-height: 200px
}
div {
  height: 26px;
  font: Arial;
  font-size: 12pt
}
form {
  height: 40px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<form>
  <input type="file" />
  <div>Choose an image to see its file signature.</div>
</form>
<hr/>
270
Drakes

Wie in anderen Antworten angegeben, können Sie den MIME-Typ überprüfen, indem Sie Signatur der Datei in den ersten Bytes der Datei überprüfen.

Was andere Antworten tun, ist das Laden der gesamten Datei in den Speicher , um die Signatur zu überprüfen. Dies ist sehr verschwenderisch und kann Ihren Browser leicht einfrieren, wenn Sie dies tun Wählen Sie eine große Datei aus Versehen oder nicht.

/**
 * Load the mime type based on the signature of the first bytes of the file
 * @param  {File}   file        A instance of File
 * @param  {Function} callback  Callback with the result
 * @author Victor www.vitim.us
 * @date   2017-03-23
 */
function loadMime(file, callback) {
    
    //List of known mimes
    var mimes = [
        {
            mime: 'image/jpeg',
            pattern: [0xFF, 0xD8, 0xFF],
            mask: [0xFF, 0xFF, 0xFF],
        },
        {
            mime: 'image/png',
            pattern: [0x89, 0x50, 0x4E, 0x47],
            mask: [0xFF, 0xFF, 0xFF, 0xFF],
        }
        // you can expand this list @see https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern
    ];

    function check(bytes, mime) {
        for (var i = 0, l = mime.mask.length; i < l; ++i) {
            if ((bytes[i] & mime.mask[i]) - mime.pattern[i] !== 0) {
                return false;
            }
        }
        return true;
    }

    var blob = file.slice(0, 4); //read the first 4 bytes of the file

    var reader = new FileReader();
    reader.onloadend = function(e) {
        if (e.target.readyState === FileReader.DONE) {
            var bytes = new Uint8Array(e.target.result);

            for (var i=0, l = mimes.length; i<l; ++i) {
                if (check(bytes, mimes[i])) return callback("Mime: " + mimes[i].mime + " <br> Browser:" + file.type);
            }

            return callback("Mime: unknown <br> Browser:" + file.type);
        }
    };
    reader.readAsArrayBuffer(blob);
}


//when selecting a file on the input
fileInput.onchange = function() {
    loadMime(fileInput.files[0], function(mime) {

        //print the output to the screen
        output.innerHTML = mime;
    });
};
<input type="file" id="fileInput">
<div id="output"></div>
8
Vitim.us

Wenn Sie nur prüfen möchten, ob es sich bei der hochgeladenen Datei um ein Bild handelt, können Sie versuchen, es in das <img> - Tag zu laden und auf Fehlerrückrufe zu prüfen.

Beispiel:

var input = document.getElementsByTagName('input')[0];
var reader = new FileReader();

reader.onload = function (e) {
    imageExists(e.target.result, function(exists){
        if (exists) {

            // Do something with the image file.. 

        } else {

            // different file format

        }
    });
};

reader.readAsDataURL(input.files[0]);


function imageExists(url, callback) {
    var img = new Image();
    img.onload = function() { callback(true); };
    img.onerror = function() { callback(false); };
    img.src = url;
}
3
Roberto14

Für alle, die dies nicht selbst implementieren möchten, hat Sindresorhus ein Hilfsprogramm erstellt, das im Browser funktioniert und die Zuordnungen von Header zu Mime für die meisten gewünschten Dokumente enthält.

https://github.com/sindresorhus/file-type

Sie können den Vorschlag von Vitim.us kombinieren, nur die ersten X Bytes einzulesen, um zu vermeiden, dass mit diesem Dienstprogramm alles in den Speicher geladen wird (Beispiel in es6):

import fileType from 'file-type'; // or wherever you load the dependency

const blob = file.slice(0, fileType.minimumBytes);

const reader = new FileReader();
reader.onloadend = function(e) {
  if (e.target.readyState !== FileReader.DONE) {
    return;
  }

  const bytes = new Uint8Array(e.target.result);
  const { ext, mime } = fileType(bytes);

  // ext is the desired extension and mime is the mimetype
};
reader.readAsArrayBuffer(blob);
3
Vinay

Das müssen Sie tun

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

Wenn Sie nach Bilddateitypen suchen möchten, wählen Sie

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

Wie Drake feststellt, könnte dies mit FileReader geschehen. Was ich hier präsentiere, ist jedoch eine funktionale Version. Beachten Sie, dass das große Problem dabei ist, die Eingabedatei zurückzusetzen. Nun, dies beschränkt sich nur auf JPG (für andere Formate müssen Sie MIME-Typ und magische Zahl ändern):

<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>

Beachten Sie, dass dies auf den neuesten Versionen von Firefox und Chrome sowie auf IExplore 10 getestet wurde.

Für eine vollständige Liste der Mimetypen siehe Wikipedia .

Für eine vollständige Liste der magischen Zahlen siehe Wikipedia .

1
lmiguelmh

Kurze Antwort ist nein.

Wie Sie sehen, leiten die Browser type von der Dateierweiterung ab. Die Mac-Vorschau scheint auch von der Erweiterung herunterzulaufen. Ich gehe davon aus, dass es schneller ist, den im Zeiger enthaltenen Dateinamen zu lesen, als die Datei auf der Festplatte nachzuschlagen und zu lesen.

Ich habe eine Kopie eines mit PNG umbenannten JPG erstellt.

Ich konnte aus beiden Bildern in chrome (sollte in modernen Browsern funktionieren) durchgehend folgendes erhalten.

ÿØÿàJFIFÿþ;CREATOR: Gd-jpeg v1.0 (using IJG JPEG v62), quality = 90

Mit dem Sie eine String.indexOf ('jpeg') Prüfung für den Bildtyp durchführen können.

Hier ist eine Geige zu erkunden http://jsfiddle.net/bamboo/jkZ2v/1/

Die ambitionierte Linie habe ich im Beispiel vergessen zu kommentieren

console.log( /^(.*)$/m.exec(window.atob( image.src.split(',')[1] )) );

  • Teilt die base64-codierten Bilddaten auf und belässt sie auf dem Bild
  • Base64 dekodiert das Bild
  • Stimmt nur mit der ersten Zeile der Bilddaten überein

Der Fiddle-Code verwendet Base64-Dekodierung, die in IE9 nicht funktioniert. Ich habe ein nettes Beispiel mit VB= Skript gefunden, das in IE funktioniert. http://blog.nihilogic.dk/2008/08/imageinfo-reading-image-metadata-with.html

Der Code zum Laden des Bildes stammt von Joel Vardy, der vor dem Hochladen einige coole Arbeiten zur Änderung der Bildgröße auf der Client-Seite ausführt, die von Interesse sein könnten hochladen

0
Lex

Hier ist eine Erweiterung der Antwort von Roberto14, die Folgendes bewirkt:

DAS ERLAUBT NUR BILDER

Überprüft, ob FileReader verfügbar ist und greift auf die Erweiterungsprüfung zurück, wenn es nicht verfügbar ist.

Gibt eine Fehlermeldung aus, wenn kein Bild vorhanden ist

Wenn es sich um ein Bild handelt, wird eine Vorschau geladen

** Sie sollten dennoch eine serverseitige Validierung durchführen. Dies ist für den Endbenutzer mehr als alles andere eine Annehmlichkeit. Aber es ist praktisch!

<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>
0
pathfinder