Wie überprüfe ich den MIME-Typ der Datei mit Javascript vor dem Hochladen?

Lesezeit: 15 Minuten

Wie uberprufe ich den MIME Typ der Datei mit Javascript vor
Fragenüberlauf

Ich habe diese und diese Fragen gelesen, was darauf hinzudeuten scheint, dass der MIME-Typ der Datei mit Javascript auf der Clientseite überprüft werden könnte. Jetzt verstehe ich, dass die eigentliche Validierung noch auf der Serverseite erfolgen muss. Ich möchte eine clientseitige Überprüfung durchführen, um eine unnötige Verschwendung von Serverressourcen zu vermeiden.

Um zu testen, ob dies auf Client-Seite möglich ist, habe ich die Erweiterung von a geändert JPEG Testdatei zu .png und wählen Sie die Datei zum Hochladen aus. 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 Oct 16 2012 10:00:00 GMT+0000 (UTC), name: “test.png”, type: “image/png”, size: 500055…}

Es zeigt Typ zu sein image/png was darauf hinzudeuten scheint, dass die Überprüfung auf der Grundlage der Dateierweiterung statt des MIME-Typs erfolgt. Ich habe Firefox 22.0 ausprobiert und es gibt mir das gleiche Ergebnis. Aber gem die W3C-Spezifikation, MIME-Schnüffeln umgesetzt werden soll.

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

  • I want to perform a client side checking to avoid unnecessary wastage of server resource. Ich verstehe nicht, warum Sie sagen, dass die Validierung auf der Serverseite erfolgen muss, aber dann sagen, dass Sie die Serverressourcen reduzieren möchten. Goldene Regel: Vertrauen Sie niemals Benutzereingaben. Was bringt es, den MIME-Typ auf der Clientseite zu überprüfen, wenn Sie dies dann nur auf der Serverseite tun. Sicherlich ist das eine “unnötige Verschwendung von Klient Ressource”?

    – Ian Clark

    18. August 2013 um 14:09 Uhr

  • Eine bessere Überprüfung des Dateityps/Feedback für Benutzer auf der Client-Seite bereitzustellen, ist eine gute Idee. Wie Sie jedoch bereits gesagt haben, verlassen sich Browser bei der Bestimmung des Werts von einfach auf die Dateierweiterungen type Eigentum für File Objekte. Der Quellcode des Webkits zum Beispiel enthüllt diese Wahrheit. Es ist möglich, Dateien clientseitig genau zu identifizieren, indem unter anderem nach „magischen Bytes“ in den Dateien gesucht wird. Ich arbeite derzeit an einer MIT-Bibliothek (in der wenigen Freizeit, die ich habe), die genau das tun wird. Wenn Sie an meinen Fortschritten interessiert sind, schauen Sie sich an github.com/rnicholus/determinator.

    – Ray Nicholus

    18. August 2013 um 14:21 Uhr

  • @IanClark, der Punkt ist, dass ich, wenn die Datei einen ungültigen Typ hat, sie auf der Clientseite ablehnen kann, anstatt die Upload-Bandbreite zu verschwenden, nur um sie auf der Serverseite abzulehnen.

    – Fragenüberlauf

    18. August 2013 um 14:55 Uhr

  • @RayNicholus, cooler Typ! Werde es mir mal anschauen wenn ich Zeit habe. Danke 🙂

    – Fragenüberlauf

    18. August 2013 um 14:57 Uhr

  • @QuestionOverflow Etwas spät, aber ich habe meiner Antwort eine vollständige Lösung und eine funktionierende Live-Demo hinzugefügt. Genießen.

    – Draken

    8. Mai 2015 um 9:00 Uhr

Wie uberprufe ich den MIME Typ der Datei mit Javascript vor
Draken

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


Überprüfen Sie, ob Ihr Browser beides unterstützt File und Blob. Alle großen 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 abrufen File Informationen von einem <input> Element wie dieses (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 obigen (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 herauskitzeln.

✘ Schnelle Methode

Sie können naiv fragen Klecks für den MIME-Typ der Datei, die er darstellt, mit diesem Muster:

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

Für Bilder kommen MIME-Typen wie folgt zurück:

Bild/JPEG
Bild/PNG

Vorbehalt: Der MIME-Typ wird anhand der Dateierweiterung erkannt und kann getäuscht oder gefälscht werden. Man kann a umbenennen .jpg zu einem .png und der MIME-Typ wird als gemeldet image/png.


✓ Richtige Header-Inspektionsmethode

Um den echten MIME-Typ einer clientseitigen Datei zu erhalten, können wir noch einen Schritt weiter gehen und die ersten paar Bytes der angegebenen Datei untersuchen, um sie mit sogenannten magische Zahlen. Seien Sie gewarnt, dass es nicht ganz einfach ist, weil zum Beispiel JPEG hat ein paar “magische Zahlen”. Dies liegt daran, dass sich das Format seit 1991 weiterentwickelt hat. Möglicherweise kommen Sie damit aus, nur die ersten beiden Bytes zu prüfen, aber ich ziehe es vor, mindestens 4 Bytes zu prüfen, um Fehlalarme 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 Dateiheaders:

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

Den echten MIME-Typ können Sie dann so ermitteln (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 basierend auf den erwarteten MIME-Typen ab.


Demo

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

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

Bildschirmfoto

Erwartete Ausgabe der 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/>

  • 2 kleine Anmerkungen. (1) Wäre es nicht besser, die Datei vor dem Lesen auf die ersten 4 Bytes zu schneiden? fileReader.readAsArrayBuffer(blob.slice(0,4))? (2) Um Dateisignaturen zu kopieren/einzufügen, sollte der Header nicht mit führenden Nullen aufgebaut werden for(var i = 0; i < bytes.length; i++) { var byte = bytes[i]; fileSignature += (byte < 10 ? "0" : "") + byte.toString(16); } ?

    – Matthäus Madson

    2. September 2015 um 19:52 Uhr


  • @Deadpool Siehe Hier. Es gibt mehr, weniger gebräuchliche JPEG-Formate von verschiedenen Herstellern. Zum Beispiel, FF D8 FF E2 = CANNON EOS JPEG-DATEI, FF D8 FF E3 = SAMSUNG D500 JPEG-DATEI. Der Schlüsselteil der JPEG-Signatur besteht nur aus 2 Bytes, aber um Fehlalarme zu reduzieren, habe ich die gängigsten 4-Byte-Signaturen hinzugefügt. Ich hoffe das hilft.

    – Draken

    4. November 2015 um 3:35 Uhr


  • Die Qualität dieser Antwort ist einfach unglaublich.

    – Lukas

    4. November 2015 um 15:04 Uhr

  • Sie müssen nicht den kompletten Blob als ArrayBuffer laden, um den mimeType zu bestimmen. Sie können die ersten 4 Bytes des Blobs einfach so schneiden und übergeben: fileReader.readAsArrayBuffer(blob.slice(0, 4))

    – codeVerine

    12. April 2018 um 13:41 Uhr

  • Was sollte die Überprüfung sein, um nur Klartext zuzulassen? Die ersten 4 Bytes für Textdateien entsprechen den ersten 4 Zeichen in der Textdatei.

    – MP-Droide

    7. Oktober 2018 um 20:25 Uhr

1646320938 115 Wie uberprufe ich den MIME Typ der Datei mit Javascript vor
Vitim.us

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

Aber was andere Antworten tun, ist Laden der gesamten Datei in den Speicher um die Signatur zu überprüfen, was sehr verschwenderisch ist und Ihren Browser leicht einfrieren könnte, wenn Sie versehentlich eine große Datei auswählen 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>

  • Ich denke readyState wird immer sein FileReader.DONE im Eventhandler (W3C-Spezifikation), auch wenn ein Fehler aufgetreten ist – sollte die Prüfung nicht if sein (!e.target.error) stattdessen?

    – Knabenhaft

    29. Juni 2018 um 15:20 Uhr


Wie uberprufe ich den MIME Typ der Datei mit Javascript vor
Wein

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

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

Sie könnten den Vorschlag von Vitim.us kombinieren, nur die ersten X Bytes einzulesen, um zu vermeiden, dass alles in den Speicher geladen wird, indem Sie dieses Dienstprogramm verwenden (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.fromBuffer(bytes);

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

  • Bei mir hat die neuste Version der Bibliothek aber nicht funktioniert "file-type": "12.4.0" funktionierte und ich musste verwenden import * as fileType from "file-type";

    – ssz

    26. April 2020 um 6:35 Uhr


Wenn Sie nur überprüfen möchten, ob die hochgeladene Datei ein Bild ist, können Sie einfach versuchen, es in zu laden <img> Markieren Sie eine Überprüfung auf einen Fehler-Callback.

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

Das müssen Sie tun

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

Wenn Sie dann nach Bilddateitypen suchen möchten

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

Wie Drake feststellt, könnte dies mit FileReader erfolgen. Was ich hier präsentiere, ist jedoch eine funktionsfähige Version. Bedenken Sie, dass das große Problem dabei mit JavaScript darin besteht, die Eingabedatei zurückzusetzen. Nun, dies beschränkt sich nur auf JPG (für andere Formate müssen Sie die Mime Typ und das magische Zahl):

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

Eine vollständige Liste der Mime-Typen finden Sie bei Wikipedia.

Eine vollständige Liste der magischen Zahlen finden Sie bei Wikipedia.

Hier ist eine Typescript-Implementierung, die webp unterstützt. Dies basiert auf der JavaScript-Antwort von Vitim.us.

interface Mime {
  mime: string;
  pattern: (number | undefined)[];
}

// tslint:disable number-literal-format
// tslint:disable no-magic-numbers
const imageMimes: Mime[] = [
  {
    mime: 'image/png',
    pattern: [0x89, 0x50, 0x4e, 0x47]
  },
  {
    mime: 'image/jpeg',
    pattern: [0xff, 0xd8, 0xff]
  },
  {
    mime: 'image/gif',
    pattern: [0x47, 0x49, 0x46, 0x38]
  },
  {
    mime: 'image/webp',
    pattern: [0x52, 0x49, 0x46, 0x46, undefined, undefined, undefined, undefined, 0x57, 0x45, 0x42, 0x50, 0x56, 0x50],
  }
  // You can expand this list @see https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern
];
// tslint:enable no-magic-numbers
// tslint:enable number-literal-format

function isMime(bytes: Uint8Array, mime: Mime): boolean {
  return mime.pattern.every((p, i) => !p || bytes[i] === p);
}

function validateImageMimeType(file: File, callback: (b: boolean) => void) {
  const numBytesNeeded = Math.max(...imageMimes.map(m => m.pattern.length));
  const blob = file.slice(0, numBytesNeeded); // Read the needed bytes of the file

  const fileReader = new FileReader();

  fileReader.onloadend = e => {
    if (!e || !fileReader.result) return;

    const bytes = new Uint8Array(fileReader.result as ArrayBuffer);

    const valid = imageMimes.some(mime => isMime(bytes, mime));

    callback(valid);
  };

  fileReader.readAsArrayBuffer(blob);
}

// When selecting a file on the input
fileInput.onchange = () => {
  const file = fileInput.files && fileInput.files[0];
  if (!file) return;

  validateImageMimeType(file, valid => {
    if (!valid) {
      alert('Not a valid image file.');
    }
  });
};

<input type="file" id="fileInput">

  • Die Frage fragt streng nach einer Lösung in Javascript. Obwohl ich verstehe, dass Typoskript leicht in Javascript konvertiert werden kann, ist dies immer noch ein zusätzlicher Schritt, der die Grenzen der ursprünglichen Frage überschreitet.

    – Wilter Monteiro

    7. Juli 2021 um 15:19 Uhr

924730cookie-checkWie überprüfe ich den MIME-Typ der Datei mit Javascript vor dem Hochladen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy