├── .gitignore ├── package.json ├── README.md ├── lib └── numbers.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | components 2 | build 3 | node_modules -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mime-sniffer", 3 | "version": "0.0.3", 4 | "description": "A mime sniffer that uses file magic numbers rather than unsecure extensions.", 5 | "dependencies": { 6 | "debug": "^2.1.0" 7 | }, 8 | "author": "Ross Zurowski", 9 | "license": "MIT", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/rosszurowski/mime-sniffer.git" 13 | }, 14 | "keywords": [ 15 | "mime", 16 | "util" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mime-sniffer 2 | 3 | A little utility to get the mime-type from binary files. 4 | 5 | Unlike [mime](https://github.com/broofa/node-mime), mime-sniffer uses [magic numbers](http://en.wikipedia.org/wiki/Magic_number_%28programming%29) instead of file extensions to get a more accurate (and less easily faked) mime-type for a given file. 6 | 7 | ## Install 8 | 9 | ```bash 10 | npm install mime-sniffer --save 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```javascript 16 | var mime = require('mime-sniffer'); 17 | 18 | mime.lookup('/path/to/file.jpg', function(err, info) { 19 | console.log(info); // { mime: 'image/jpeg', extension: 'jpg' } 20 | }); 21 | ``` 22 | 23 | You can also pass in a Buffer: 24 | 25 | ```javascript 26 | var fs = require('fs'); 27 | var mime = require('mime-sniffer'); 28 | 29 | mime.lookup(fs.readFileSync('/path/to/file.jpg'), function(err, info) { 30 | // { mime: 'image/jpeg', extension: 'jpg' } 31 | }); 32 | ``` 33 | 34 | ## Supported files 35 | To see what file types are supported, take a peek in the `lib/numbers.js` file. 36 | 37 | Currently, mime-sniffer supports: 38 | 39 | #### Images 40 | - gif 41 | - png 42 | - jpg 43 | - webp 44 | - tiff 45 | - bmp 46 | 47 | #### Video 48 | - mp4 49 | - mov 50 | - webm 51 | 52 | #### Audio 53 | - mp3 54 | - ogg 55 | - flac 56 | - wav 57 | 58 | #### Misc 59 | - pdf 60 | 61 | ## License 62 | 63 | MIT -------------------------------------------------------------------------------- /lib/numbers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A set of magic numbers for different file types 3 | * Comprised from: 4 | * http://www.garykessler.net/library/file_sigs.html 5 | * http://en.wikipedia.org/wiki/List_of_file_signatures 6 | */ 7 | 8 | module.exports = { 9 | // gif 10 | '474946383961': { mime: 'image/gif', extension: 'gif' }, 11 | '474946383761': { mime: 'image/gif', extension: 'gif' }, 12 | // png 13 | '89504E470D0A1A0A': { mime: 'image/png', extension: 'png' }, 14 | // jpg 15 | 'FFD8FF': { mime: 'image/jpeg', extension: 'jpg' }, 16 | // webp 17 | '57454250': { mime: 'image/webp', extension: 'webp' }, 18 | // tiff 19 | '49492A00': { mime: 'image/tiff', extension: 'tiff' }, 20 | '4D4D002A': { mime: 'image/tiff', extension: 'tiff' }, 21 | // bmp 22 | '424D': { mime: 'image/bmp', extension: 'bmp' }, 23 | 24 | // mp4 25 | '000000146674797069736F6D': { mime: 'video/mp4', extension: 'mp4' }, 26 | '000000186674797033677035': { mime: 'video/mp4', extension: 'mp4' }, 27 | // mov 28 | '000000146674797071742020': { mime: 'video/quicktime', extension: 'mov'}, 29 | // webm 30 | '1A45DFA3': { mime: 'video/webm', extension: 'webm' }, 31 | 32 | // mp3 33 | 'FFFB': { mime: 'audio/mpeg', extension: 'mp3' }, 34 | '494433': { mime: 'audio/mpeg', extension: 'mp3' }, 35 | // ogg 36 | '4F676753': { mime: 'audio/ogg', extension: 'ogg' }, 37 | // flac 38 | '664C6143': { mime: 'audio/x-flac', extension: 'flac' }, 39 | // wav 40 | '52494646': { mime: 'audio/x-wav', extension: 'wav' }, 41 | 42 | // pdf 43 | '25504446': { mime: 'application/pdf', extension: 'pdf' }, 44 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A mime-type sniffer for binary files by using magic numbers instead of 3 | * just reading the extensions 4 | * 5 | */ 6 | 7 | var debug = require('debug')('mime-sniffer'); 8 | var fs = require('fs'); 9 | 10 | // the length of bytes to check for the magic numbers 11 | var length = 24; 12 | // the database of magic numbers 13 | var map = exports.database = require('./lib/numbers.js'); 14 | var keys = Object.keys(map); 15 | 16 | /** 17 | * Look up file info 18 | * @param {String|Buffer} file 19 | * @param {Function} done (err, info) 20 | */ 21 | exports.lookup = function(file, done) { 22 | 23 | if (!file) return done(new Error('Must pass a file path or buffer')); 24 | var buffer = new Buffer(length); 25 | 26 | if (Buffer.isBuffer(file)) { 27 | file.copy(buffer, 0, 0, length); 28 | return check(); 29 | } 30 | 31 | fs.open(file, 'r', function(stat, fd) { 32 | fs.read(fd, buffer, 0, length, 0, check); 33 | }); 34 | 35 | function check(err) { 36 | if (err) return done(err); 37 | var magic = buffer.toString('hex'); 38 | // if the keys contain the magic number somewhere 39 | var key = contains(keys, magic); 40 | var result = key ? map[key] : false; 41 | if (!result) { 42 | debug('No magic number match for:' + ' ' + magic); 43 | return done(new Error('No magic number match for ' + file)); 44 | } 45 | return done(null, result); 46 | } 47 | } 48 | 49 | /** 50 | * Check if an element in the array contains the given search. 51 | * If it matches, we return the item, otherwise we return false 52 | * 53 | * @param {Array} arr 54 | * @param {String} search 55 | * @returns {Object} 56 | */ 57 | function contains(arr, search) { 58 | for (var i = 0, l = arr.length; i < l; i++) { 59 | var value = arr[i]; 60 | if (!!~search.toLowerCase().indexOf(value.toLowerCase())) { 61 | return value; 62 | } 63 | } 64 | return false; 65 | } --------------------------------------------------------------------------------