Skip to content

Commit a1acb89

Browse files
committed
Initial commit
1 parent 595db46 commit a1acb89

File tree

2 files changed

+117
-0
lines changed

2 files changed

+117
-0
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,28 @@
11
# filevalidator.js
22
File signature validation in JavaScript
3+
4+
Useful for cases where looking at a filename extension only is not
5+
reliable enough.
6+
7+
Usage:
8+
9+
10+
```js
11+
define([
12+
'filevalidator',
13+
],
14+
15+
function(filevalidator) {
16+
17+
var fileInput = document.getElementById('file-input');
18+
19+
fileInput.addEventListener('change', function(e) {
20+
var file = e.currentTarget.files[0];
21+
FileDetector.verifyFileType(file, ['mp3', 'wav'], function(valid) {
22+
alert('Valid mp3 or wave file: ' + !!valid);
23+
});
24+
});
25+
26+
});
27+
```
28+

filevalidator.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
define([
2+
'underscore',
3+
],
4+
5+
function(_) {
6+
'use strict';
7+
8+
// https://en.wikipedia.org/wiki/List_of_file_signatures
9+
var fileSignatures = {
10+
'mp3': [
11+
// MPEG-1 Layer 3 file without an ID3 tag or with an ID3v1 tag (which's appended at the end of the file)
12+
[0xFF, 0xFB],
13+
// MP3 file with an ID3v2 container
14+
[0x49, 0x44, 0x33]
15+
],
16+
'wav': [
17+
// Waveform Audio File Format
18+
// Empty slots can be any byte. Can't look at only first 4 or else .avi files match
19+
[0x52, 0x49, 0x46, 0x46, , , , , 0x57, 0x41, 0x56, 0x45]
20+
],
21+
};
22+
23+
/**
24+
* Compare an Uint8Array with an expected file signature.
25+
* Can't do a direct equality check since some signatures (e.gfor wav files) have wildcard slots.
26+
* @param {array} sig - pattern from fileSignatures
27+
* @param {Uint8Array} actual - bytes from file (should already be sliced to match length of sig)
28+
* @returns {boolean} - do they match
29+
*/
30+
var compareSignature = function(sig, actual) {
31+
if (sig.length !== actual.length) return false;
32+
for (var i = 0, l = sig.length; i < l; i++) {
33+
if (sig[i] !== actual[i] && typeof sig[i] !== 'undefined') return false;
34+
}
35+
return true;
36+
};
37+
38+
/**
39+
* @param {Uint8Array} uint8
40+
* @param {string} type
41+
* @returns {boolean}
42+
*/
43+
var matchesFileType = function(uint8, type) {
44+
return _.find(fileSignatures[type], function(sig) {
45+
return compareSignature(sig, uint8.subarray(0, sig.length));
46+
});
47+
};
48+
49+
/**
50+
* Detect, through file signature / mime sniffing detection, if a given File
51+
* matches an expected type or types. The types supported are the keys in
52+
* fileSignatures above.
53+
* @param {File} file
54+
* @param {(string|string[])} types - e.g. 'mp3' or ['mp3', 'wav']
55+
* @param {function} - callback which is passed a boolean
56+
*/
57+
var verifyFileType = function(file, types, cb) {
58+
if (_.isString(types)) types = [types];
59+
60+
// Calculate the longest file signature for any of the requested
61+
// types, so we know how many bytes of this file to look at.
62+
var bytesNeeded = types.reduce(function(prevMax, type, idx, arr) {
63+
var sigs = fileSignatures[type];
64+
return Math.max.apply(this, [prevMax].concat(sigs.map(function(sig) {
65+
return sig.length;
66+
})));
67+
}, 0);
68+
69+
// Load file into ArrayBuffer and see if its first few bytes match
70+
// the signature of any of our requested types. Let callback know.
71+
var reader = new FileReader();
72+
reader.onload = function(e) {
73+
// Load only as many bytes from the array buffer as necessary
74+
var arrayBuffer = e.currentTarget.result;
75+
var bytes = new Uint8Array(arrayBuffer, 0, bytesNeeded);
76+
var match = _.find(types, function(type) {
77+
return matchesFileType(bytes, type);
78+
});
79+
cb(match);
80+
};
81+
reader.readAsArrayBuffer(file);
82+
};
83+
84+
// Expose public interface
85+
var FileDetector = {
86+
verifyFileType: verifyFileType
87+
};
88+
89+
return FileDetector;
90+
});
91+

0 commit comments

Comments
 (0)