mirror of
https://github.com/h2non/filetype.git
synced 2025-12-24 11:52:08 +08:00
feat: initial commit
This commit is contained in:
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@@ -0,0 +1,12 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tabs
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/bimg
|
||||
/bundle
|
||||
bin
|
||||
/*.jpg
|
||||
/*.png
|
||||
/*.webp
|
||||
/fixtures/*_out.*
|
||||
7
.travis.yml
Normal file
7
.travis.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.5
|
||||
- 1.4
|
||||
- 1.3
|
||||
- release
|
||||
- tip
|
||||
24
LICENSE
Normal file
24
LICENSE
Normal file
@@ -0,0 +1,24 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) Tomas Aparicio
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
41
README.md
Normal file
41
README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# filetype [](https://travis-ci.org/h2non/filetype) [](https://godoc.org/github.com/h2non/filetype)
|
||||
|
||||
Small [Go](https://golang.org) package to infer the file type checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of a given binary buffer.
|
||||
|
||||
Supports a wide range of file types, including images formats, fonts, videos, audio and other common application files, and provides the proper file extension and convenient MIME code.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go get gopkg.in/h2non/filetype.v0
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"gopkg.in/h2non/filetype.v0"
|
||||
)
|
||||
|
||||
func main() {
|
||||
buf, _ := ioutil.ReadFile("sample.jpg")
|
||||
|
||||
kind, unkwown := filetype.Type(buf)
|
||||
if unkwown != nil {
|
||||
fmt.Printf("Unkwown file type")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("File type found: %s. MIME: %s", kind.Extension, kind.MIME.Value)
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MIT - Tomas Aparicio
|
||||
116
filetype.go
Normal file
116
filetype.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package filetype
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"gopkg.in/h2non/filetype.v0/matchers"
|
||||
"gopkg.in/h2non/filetype.v0/types"
|
||||
)
|
||||
|
||||
// Map of extensions and file types
|
||||
var Types = types.Types
|
||||
|
||||
// Map of file matchers
|
||||
var Matchers = matchers.Matchers
|
||||
|
||||
// Default types
|
||||
var Empty = types.Empty
|
||||
var Unknown = types.Unknown
|
||||
|
||||
// Predefined errors
|
||||
var EmptyBufferErr = errors.New("Empty buffer")
|
||||
var UnknownBufferErr = errors.New("Unknown buffer type")
|
||||
|
||||
func DoMatch(buf []byte) (types.Type, error) {
|
||||
return matchers.Match(buf)
|
||||
}
|
||||
|
||||
// Infer the file type of a buffer inspecting the magic numbers
|
||||
func Match(buf []byte) (types.Type, error) {
|
||||
return DoMatch(buf)
|
||||
}
|
||||
|
||||
// Alias to Match()
|
||||
func Type(buf []byte) (types.Type, error) {
|
||||
return Match(buf)
|
||||
}
|
||||
|
||||
func doMatchMap(buf []byte, machers matchers.Map) (types.Type, error) {
|
||||
kind := matchers.MatchMap(buf, machers)
|
||||
if kind != types.Unknown {
|
||||
return kind, nil
|
||||
}
|
||||
return kind, UnknownBufferErr
|
||||
}
|
||||
|
||||
// Match file as image type
|
||||
func Image(buf []byte) (types.Type, error) {
|
||||
return doMatchMap(buf, matchers.Image)
|
||||
}
|
||||
|
||||
// Match file as audio type
|
||||
func Audio(buf []byte) (types.Type, error) {
|
||||
return doMatchMap(buf, matchers.Audio)
|
||||
}
|
||||
|
||||
// Match file as video type
|
||||
func Video(buf []byte) (types.Type, error) {
|
||||
return doMatchMap(buf, matchers.Audio)
|
||||
}
|
||||
|
||||
// Match file as text font type
|
||||
func Font(buf []byte) (types.Type, error) {
|
||||
return doMatchMap(buf, matchers.Font)
|
||||
}
|
||||
|
||||
// Match file as generic archive type
|
||||
func Archive(buf []byte) (types.Type, error) {
|
||||
return doMatchMap(buf, matchers.Archive)
|
||||
}
|
||||
|
||||
func Is(buf []byte, ext string) bool {
|
||||
kind, ok := types.Types[ext]
|
||||
if ok {
|
||||
return IsType(buf, kind)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsType(buf []byte, kind types.Type) bool {
|
||||
matcher := matchers.Matchers[kind]
|
||||
if matcher == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
length := len(buf)
|
||||
return matcher(buf, length) != types.Unknown
|
||||
}
|
||||
|
||||
// Register a new matcher type
|
||||
func AddMatcher(fileType types.Type, matcher matchers.Matcher) matchers.TypeMatcher {
|
||||
return matchers.NewMatcher(fileType, matcher)
|
||||
}
|
||||
|
||||
// Register a new file type
|
||||
func AddType(ext, mime string) types.Type {
|
||||
return types.NewType(ext, mime)
|
||||
}
|
||||
|
||||
// Check if a given file extension is supported
|
||||
func IsSupported(ext string) bool {
|
||||
for name, _ := range Types {
|
||||
if name == ext {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if a given MIME expression is supported
|
||||
func IsMIMESupported(mime string) bool {
|
||||
for _, m := range Types {
|
||||
if m.MIME.Value == mime {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
25
filetype_test.go
Normal file
25
filetype_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package filetype
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMatches(t *testing.T) {
|
||||
cases := []struct {
|
||||
buf []byte
|
||||
ext string
|
||||
}{
|
||||
{[]byte{0xFF, 0xD8, 0xFF}, "jpg"},
|
||||
}
|
||||
|
||||
for _, test := range cases {
|
||||
match, err := Match(test.buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
|
||||
if match.Extension != test.ext {
|
||||
t.Fatalf("Invalid image type: %s", match.Extension)
|
||||
}
|
||||
}
|
||||
}
|
||||
134
matchers/archive.go
Normal file
134
matchers/archive.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package matchers
|
||||
|
||||
var TypeEpub = NewType("epub", "application/epub+zip")
|
||||
var TypeZip = NewType("zip", "application/zip")
|
||||
var TypeTar = NewType("tar", "application/x-tar")
|
||||
var TypeRar = NewType("rar", "application/x-rar-compressed")
|
||||
var TypeGz = NewType("gz", "application/gzip")
|
||||
var TypeBz2 = NewType("bz2", "application/x-bzip2")
|
||||
var Type7z = NewType("7z", "application/x-7z-compressed")
|
||||
var TypeXz = NewType("xz", "application/x-xz")
|
||||
var TypePdf = NewType("pdf", "application/pdf")
|
||||
var TypeExe = NewType("exe", "application/x-msdownload")
|
||||
var TypeSwf = NewType("swf", "application/x-shockwave-flash")
|
||||
var TypeRtf = NewType("rtf", "application/rtf")
|
||||
var TypeEot = NewType("eot", "application/octet-stream")
|
||||
var TypePs = NewType("ps", "application/postscript")
|
||||
var TypeSqlite = NewType("sqlite", "application/x-sqlite3")
|
||||
|
||||
var Archive = Map{
|
||||
TypeEpub: Epub,
|
||||
TypeZip: Zip,
|
||||
TypeTar: Tar,
|
||||
TypeRar: Rar,
|
||||
TypeBz2: Bz2,
|
||||
Type7z: SevenZ,
|
||||
TypeXz: Xz,
|
||||
TypePdf: Pdf,
|
||||
TypeExe: Exe,
|
||||
TypeSwf: Swf,
|
||||
TypeRtf: Rtf,
|
||||
TypeEot: Eot,
|
||||
TypePs: Ps,
|
||||
TypeSqlite: Sqlite,
|
||||
}
|
||||
|
||||
func Epub(buf []byte, length int) bool {
|
||||
return length > 57 &&
|
||||
buf[0] == 0x50 && buf[1] == 0x4B && buf[2] == 0x3 && buf[3] == 0x4 &&
|
||||
buf[30] == 0x6D && buf[31] == 0x69 && buf[32] == 0x6D && buf[33] == 0x65 &&
|
||||
buf[34] == 0x74 && buf[35] == 0x79 && buf[36] == 0x70 && buf[37] == 0x65 &&
|
||||
buf[38] == 0x61 && buf[39] == 0x70 && buf[40] == 0x70 && buf[41] == 0x6C &&
|
||||
buf[42] == 0x69 && buf[43] == 0x63 && buf[44] == 0x61 && buf[45] == 0x74 &&
|
||||
buf[46] == 0x69 && buf[47] == 0x6F && buf[48] == 0x6E && buf[49] == 0x2F &&
|
||||
buf[50] == 0x65 && buf[51] == 0x70 && buf[52] == 0x75 && buf[53] == 0x62 &&
|
||||
buf[54] == 0x2B && buf[55] == 0x7A && buf[56] == 0x69 && buf[57] == 0x70
|
||||
}
|
||||
|
||||
func Zip(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
buf[0] == 0x50 && buf[1] == 0x4B &&
|
||||
(buf[2] == 0x3 || buf[2] == 0x5 || buf[2] == 0x7) &&
|
||||
(buf[3] == 0x4 || buf[3] == 0x6 || buf[3] == 0x8)
|
||||
}
|
||||
|
||||
func Tar(buf []byte, length int) bool {
|
||||
return length > 261 &&
|
||||
buf[257] == 0x75 && buf[258] == 0x73 &&
|
||||
buf[259] == 0x74 && buf[260] == 0x61 &&
|
||||
buf[261] == 0x72
|
||||
}
|
||||
|
||||
func Rar(buf []byte, length int) bool {
|
||||
return length > 6 &&
|
||||
buf[0] == 0x52 && buf[1] == 0x61 && buf[2] == 0x72 &&
|
||||
buf[3] == 0x21 && buf[4] == 0x1A && buf[5] == 0x7 &&
|
||||
(buf[6] == 0x0 || buf[6] == 0x1)
|
||||
}
|
||||
|
||||
func Gz(buf []byte, length int) bool {
|
||||
return length > 2 &&
|
||||
buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x8
|
||||
}
|
||||
|
||||
func Bz2(buf []byte, length int) bool {
|
||||
return length > 2 &&
|
||||
buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68
|
||||
}
|
||||
|
||||
func SevenZ(buf []byte, length int) bool {
|
||||
return length > 5 &&
|
||||
buf[0] == 0x37 && buf[1] == 0x7A && buf[2] == 0xBC &&
|
||||
buf[3] == 0xAF && buf[4] == 0x27 && buf[5] == 0x1C
|
||||
}
|
||||
|
||||
func Pdf(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
buf[0] == 0x25 && buf[1] == 0x50 &&
|
||||
buf[2] == 0x44 && buf[3] == 0x46
|
||||
}
|
||||
|
||||
func Exe(buf []byte, length int) bool {
|
||||
return length > 1 &&
|
||||
buf[0] == 0x4D && buf[1] == 0x5A
|
||||
}
|
||||
|
||||
func Swf(buf []byte, length int) bool {
|
||||
return length > 2 &&
|
||||
(buf[0] == 0x43 || buf[0] == 0x46) &&
|
||||
buf[1] == 0x57 && buf[2] == 0x53
|
||||
}
|
||||
|
||||
func Rtf(buf []byte, length int) bool {
|
||||
return length > 4 &&
|
||||
buf[0] == 0x7B && buf[1] == 0x5C &&
|
||||
buf[2] == 0x72 && buf[3] == 0x74 &&
|
||||
buf[4] == 0x66
|
||||
}
|
||||
|
||||
func Eot(buf []byte, length int) bool {
|
||||
return length > 10 &&
|
||||
buf[34] == 0x4C && buf[35] == 0x50 &&
|
||||
((buf[8] == 0x02 && buf[9] == 0x00 &&
|
||||
buf[10] == 0x01) || (buf[8] == 0x01 &&
|
||||
buf[9] == 0x00 && buf[10] == 0x00) ||
|
||||
(buf[8] == 0x02 && buf[9] == 0x00 && buf[10] == 0x02))
|
||||
}
|
||||
|
||||
func Ps(buf []byte, length int) bool {
|
||||
return length > 1 &&
|
||||
buf[0] == 0x25 && buf[1] == 0x21
|
||||
}
|
||||
|
||||
func Xz(buf []byte, length int) bool {
|
||||
return length > 5 &&
|
||||
buf[0] == 0xFD && buf[1] == 0x37 &&
|
||||
buf[2] == 0x7A && buf[3] == 0x58 &&
|
||||
buf[4] == 0x5A && buf[5] == 0x00
|
||||
}
|
||||
|
||||
func Sqlite(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
buf[0] == 0x53 && buf[1] == 0x51 &&
|
||||
buf[2] == 0x4C && buf[3] == 0x69
|
||||
}
|
||||
54
matchers/audio.go
Normal file
54
matchers/audio.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package matchers
|
||||
|
||||
var TypeMidi = NewType("mid", "audio/midi")
|
||||
var TypeMp3 = NewType("mp3", "audio/mpeg")
|
||||
var TypeM4a = NewType("m4a", "audio/m4a")
|
||||
var TypeOgg = NewType("ogg", "audio/ogg")
|
||||
var TypeFlac = NewType("flac", "audio/x-flac")
|
||||
var TypeWav = NewType("wav", "audio/x-wav")
|
||||
|
||||
var Audio = Map{
|
||||
TypeMidi: Midi,
|
||||
TypeMp3: Mp3,
|
||||
TypeM4a: M4a,
|
||||
TypeOgg: Ogg,
|
||||
TypeFlac: Flac,
|
||||
TypeWav: Wav,
|
||||
}
|
||||
|
||||
func Midi(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
buf[0] == 0x4D && buf[1] == 0x54 &&
|
||||
buf[2] == 0x68 && buf[3] == 0x64
|
||||
}
|
||||
|
||||
func Mp3(buf []byte, length int) bool {
|
||||
return length > 2 &&
|
||||
(buf[0] == 0x49 && buf[1] == 0x44 && buf[2] == 0x33) ||
|
||||
(buf[0] == 0xFF && buf[1] == 0xfb)
|
||||
}
|
||||
|
||||
func M4a(buf []byte, length int) bool {
|
||||
return length > 10 &&
|
||||
(buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 &&
|
||||
buf[7] == 0x70 && buf[8] == 0x4D && buf[9] == 0x34 && buf[10] == 0x41) ||
|
||||
(buf[0] == 0x4D && buf[1] == 0x34 && buf[2] == 0x41 && buf[3] == 0x20)
|
||||
}
|
||||
|
||||
func Ogg(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
buf[0] == 0x4F && buf[1] == 0x67 &&
|
||||
buf[2] == 0x67 && buf[3] == 0x53
|
||||
}
|
||||
|
||||
func Flac(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
buf[0] == 0x66 && buf[1] == 0x4C &&
|
||||
buf[2] == 0x61 && buf[3] == 0x43
|
||||
}
|
||||
|
||||
func Wav(buf []byte, length int) bool {
|
||||
return length > 11 &&
|
||||
buf[0] == 0x52 && buf[1] == 0x49 && buf[2] == 0x46 && buf[3] == 0x46 &&
|
||||
buf[8] == 0x57 && buf[9] == 0x41 && buf[10] == 0x56 && buf[11] == 0x45
|
||||
}
|
||||
39
matchers/fonts.go
Normal file
39
matchers/fonts.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package matchers
|
||||
|
||||
var TypeWoff = NewType("woff", "application/font-woff")
|
||||
var TypeWoff2 = NewType("woff2", "application/font-woff")
|
||||
var TypeTtf = NewType("ttf", "application/font-sfnt")
|
||||
var TypeOtf = NewType("otf", "application/font-sfnt")
|
||||
|
||||
var Font = Map{
|
||||
TypeWoff: Woff,
|
||||
TypeWoff2: Woff2,
|
||||
TypeTtf: Ttf,
|
||||
TypeOtf: Otf,
|
||||
}
|
||||
|
||||
func Woff(buf []byte, length int) bool {
|
||||
return length > 7 &&
|
||||
buf[0] == 0x77 && buf[1] == 0x4F && buf[2] == 0x46 && buf[3] == 0x46 &&
|
||||
buf[4] == 0x00 && buf[5] == 0x01 && buf[6] == 0x00 && buf[7] == 0x00
|
||||
}
|
||||
|
||||
func Woff2(buf []byte, length int) bool {
|
||||
return length > 7 &&
|
||||
buf[0] == 0x77 && buf[1] == 0x4F && buf[2] == 0x46 && buf[3] == 0x32 &&
|
||||
buf[4] == 0x00 && buf[5] == 0x01 && buf[6] == 0x00 && buf[7] == 0x00
|
||||
}
|
||||
|
||||
func Ttf(buf []byte, length int) bool {
|
||||
return length > 4 &&
|
||||
buf[0] == 0x00 && buf[1] == 0x01 &&
|
||||
buf[2] == 0x00 && buf[3] == 0x00 &&
|
||||
buf[4] == 0x00
|
||||
}
|
||||
|
||||
func Otf(buf []byte, length int) bool {
|
||||
return length > 4 &&
|
||||
buf[0] == 0x4F && buf[1] == 0x54 &&
|
||||
buf[2] == 0x54 && buf[3] == 0x4F &&
|
||||
buf[4] == 0x00
|
||||
}
|
||||
82
matchers/image.go
Normal file
82
matchers/image.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package matchers
|
||||
|
||||
var TypeJpeg = NewType("jpg", "image/jpeg")
|
||||
var TypePng = NewType("png", "image/png")
|
||||
var TypeGif = NewType("gif", "image/gif")
|
||||
var TypeWebp = NewType("webp", "image/webp")
|
||||
var TypeCR2 = NewType("cr2", "image/x-canon-cr2")
|
||||
var TypeTiff = NewType("tif", "image/tiff")
|
||||
var TypeBmp = NewType("bmp", "image/bmp")
|
||||
var TypeJxr = NewType("jxr", "image/vnd.ms-photo")
|
||||
var TypePsd = NewType("psd", "image/vnd.adobe.photoshop")
|
||||
var TypeIco = NewType("ico", "image/x-icon")
|
||||
|
||||
var Image = Map{
|
||||
TypeJpeg: Jpeg,
|
||||
TypePng: Png,
|
||||
TypeGif: Gif,
|
||||
TypeWebp: Webp,
|
||||
TypeCR2: CR2,
|
||||
TypeTiff: Tiff,
|
||||
TypeBmp: Bmp,
|
||||
TypeJxr: Jxr,
|
||||
TypePsd: Psd,
|
||||
TypeIco: Ico,
|
||||
}
|
||||
|
||||
func Jpeg(buf []byte, length int) bool {
|
||||
return length > 2 &&
|
||||
buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF
|
||||
}
|
||||
|
||||
func Png(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
buf[0] == 0x89 && buf[1] == 0x50 &&
|
||||
buf[2] == 0x4E && buf[3] == 0x47
|
||||
}
|
||||
|
||||
func Gif(buf []byte, length int) bool {
|
||||
return length > 2 &&
|
||||
buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46
|
||||
}
|
||||
|
||||
func Webp(buf []byte, length int) bool {
|
||||
return length > 11 &&
|
||||
buf[8] == 0x57 && buf[9] == 0x45 &&
|
||||
buf[10] == 0x42 && buf[11] == 0x50
|
||||
}
|
||||
|
||||
func CR2(buf []byte, length int) bool {
|
||||
return length > 9 &&
|
||||
((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) ||
|
||||
(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) &&
|
||||
buf[8] == 0x43 && buf[9] == 0x52
|
||||
}
|
||||
|
||||
func Tiff(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
(buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) ||
|
||||
(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)
|
||||
}
|
||||
|
||||
func Bmp(buf []byte, length int) bool {
|
||||
return length > 1 &&
|
||||
buf[0] == 0x42 && buf[1] == 0x4D
|
||||
}
|
||||
|
||||
func Jxr(buf []byte, length int) bool {
|
||||
return length > 2 &&
|
||||
buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0xBC
|
||||
}
|
||||
|
||||
func Psd(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
buf[0] == 0x38 && buf[1] == 0x42 &&
|
||||
buf[2] == 0x50 && buf[3] == 0x53
|
||||
}
|
||||
|
||||
func Ico(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
buf[0] == 0x00 && buf[1] == 0x00 &&
|
||||
buf[2] == 0x01 && buf[3] == 0x00
|
||||
}
|
||||
80
matchers/matcher.go
Normal file
80
matchers/matcher.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package matchers
|
||||
|
||||
import "gopkg.in/h2non/filetype.v0/types"
|
||||
|
||||
type Map map[types.Type]Matcher
|
||||
|
||||
// Matcher function interface as type alias
|
||||
type Matcher func([]byte, int) bool
|
||||
|
||||
// Type specific matcher function interface
|
||||
type TypeMatcher func([]byte, int) types.Type
|
||||
|
||||
// Store registered file type matchers
|
||||
var Matchers = make(map[types.Type]TypeMatcher)
|
||||
|
||||
var NewType = types.NewType
|
||||
|
||||
// Create and register a new type matcher function
|
||||
func NewMatcher(kind types.Type, fn Matcher) TypeMatcher {
|
||||
matcher := func(buf []byte, length int) types.Type {
|
||||
if fn(buf, length) {
|
||||
return kind
|
||||
}
|
||||
return types.Unknown
|
||||
}
|
||||
Matchers[kind] = matcher
|
||||
return matcher
|
||||
}
|
||||
|
||||
// Match the file type of a given buffer
|
||||
func Match(buf []byte) (types.Type, error) {
|
||||
length := len(buf)
|
||||
if length == 0 {
|
||||
return types.Empty, nil
|
||||
}
|
||||
|
||||
for _, checker := range Matchers {
|
||||
match := checker(buf, length)
|
||||
if match != types.Unknown && match.Extension != "" {
|
||||
return match, nil
|
||||
}
|
||||
}
|
||||
|
||||
return types.Unknown, nil
|
||||
}
|
||||
|
||||
func MatchType(buf []byte, kind types.Type) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func MatchMap(buf []byte, matchers Map) types.Type {
|
||||
length := len(buf)
|
||||
for kind, matcher := range matchers {
|
||||
if matcher(buf, length) {
|
||||
return kind
|
||||
}
|
||||
}
|
||||
return types.Unknown
|
||||
}
|
||||
|
||||
func MatchesMap(buf []byte, matchers Map) bool {
|
||||
return MatchMap(buf, matchers) != types.Unknown
|
||||
}
|
||||
|
||||
func Matches(buf []byte) bool {
|
||||
kind, _ := Match(buf)
|
||||
return kind != types.Unknown
|
||||
}
|
||||
|
||||
func register(matchers ...Map) {
|
||||
for _, m := range matchers {
|
||||
for kind, matcher := range m {
|
||||
NewMatcher(kind, matcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
register(Image, Video, Audio, Font, Archive)
|
||||
}
|
||||
97
matchers/video.go
Normal file
97
matchers/video.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package matchers
|
||||
|
||||
var TypeMp4 = NewType("mp4", "video/mp4")
|
||||
var TypeM4v = NewType("m4v", "video/x-m4v")
|
||||
var TypeMkv = NewType("mkv", "video/x-matroska")
|
||||
var TypeWebm = NewType("webm", "video/webm")
|
||||
var TypeMov = NewType("mov", "video/quicktime")
|
||||
var TypeAvi = NewType("avi", "video/x-msvideo")
|
||||
var TypeWmv = NewType("wmv", "video/x-ms-wmv")
|
||||
var TypeMpeg = NewType("mpg", "video/mpeg")
|
||||
var TypeFlv = NewType("flv", "video/x-flv")
|
||||
|
||||
var Video = Map{
|
||||
TypeMp4: Mp4,
|
||||
TypeM4v: M4v,
|
||||
TypeMkv: Mkv,
|
||||
TypeMov: Mov,
|
||||
TypeAvi: Avi,
|
||||
TypeWmv: Wmv,
|
||||
TypeMpeg: Mpeg,
|
||||
TypeFlv: Flv,
|
||||
}
|
||||
|
||||
func M4v(buf []byte, length int) bool {
|
||||
return length > 10 &&
|
||||
buf[0] == 0x0 && buf[1] == 0x0 &&
|
||||
buf[2] == 0x0 && buf[3] == 0x1C &&
|
||||
buf[4] == 0x66 && buf[5] == 0x74 &&
|
||||
buf[6] == 0x79 && buf[7] == 0x70 &&
|
||||
buf[8] == 0x4D && buf[9] == 0x34 &&
|
||||
buf[10] == 0x56
|
||||
}
|
||||
|
||||
func Mkv(buf []byte, length int) bool {
|
||||
return length > 38 &&
|
||||
buf[31] == 0x6D && buf[32] == 0x61 &&
|
||||
buf[33] == 0x74 && buf[34] == 0x72 &&
|
||||
buf[35] == 0x6f && buf[36] == 0x73 &&
|
||||
buf[37] == 0x6B && buf[38] == 0x61
|
||||
}
|
||||
|
||||
func Webm(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
buf[0] == 0x1A && buf[1] == 0x45 &&
|
||||
buf[2] == 0xDF && buf[3] == 0xA3
|
||||
}
|
||||
|
||||
func Mov(buf []byte, length int) bool {
|
||||
return length > 7 &&
|
||||
buf[0] == 0x0 && buf[1] == 0x0 &&
|
||||
buf[2] == 0x0 && buf[3] == 0x14 &&
|
||||
buf[4] == 0x66 && buf[5] == 0x74 &&
|
||||
buf[6] == 0x79 && buf[7] == 0x70
|
||||
}
|
||||
|
||||
func Avi(buf []byte, length int) bool {
|
||||
return length > 10 &&
|
||||
buf[0] == 0x52 && buf[1] == 0x49 &&
|
||||
buf[2] == 0x46 && buf[3] == 0x46 &&
|
||||
buf[8] == 0x41 && buf[9] == 0x56 &&
|
||||
buf[10] == 0x49
|
||||
}
|
||||
|
||||
func Wmv(buf []byte, length int) bool {
|
||||
return length > 9 &&
|
||||
buf[0] == 0x30 && buf[1] == 0x26 &&
|
||||
buf[2] == 0xB2 && buf[3] == 0x75 &&
|
||||
buf[4] == 0x8E && buf[5] == 0x66 &&
|
||||
buf[6] == 0xCF && buf[7] == 0x11 &&
|
||||
buf[8] == 0xA6 && buf[9] == 0xD9
|
||||
}
|
||||
|
||||
func Mpeg(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x1 &&
|
||||
buf[3] >= 0xb0 && buf[3] <= 0xbf
|
||||
}
|
||||
|
||||
func Flv(buf []byte, length int) bool {
|
||||
return length > 3 &&
|
||||
buf[0] == 0x46 && buf[1] == 0x4C &&
|
||||
buf[2] == 0x56 && buf[3] == 0x01
|
||||
}
|
||||
|
||||
func Mp4(buf []byte, length int) bool {
|
||||
return length > 27 &&
|
||||
(buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x0 &&
|
||||
(buf[3] == 0x18 || buf[3] == 0x20) && buf[4] == 0x66 &&
|
||||
buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70) ||
|
||||
(buf[0] == 0x33 && buf[1] == 0x67 && buf[2] == 0x70 && buf[3] == 0x35) ||
|
||||
(buf[0] == 0x0 && buf[1] == 0x0 && buf[2] == 0x0 && buf[3] == 0x1C &&
|
||||
buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70 &&
|
||||
buf[8] == 0x6D && buf[9] == 0x70 && buf[10] == 0x34 && buf[11] == 0x32 &&
|
||||
buf[16] == 0x6D && buf[17] == 0x70 && buf[18] == 0x34 && buf[19] == 0x31 &&
|
||||
buf[20] == 0x6D && buf[21] == 0x70 && buf[22] == 0x34 && buf[23] == 0x32 &&
|
||||
buf[24] == 0x69 && buf[25] == 0x73 && buf[26] == 0x6F && buf[27] == 0x6D)
|
||||
}
|
||||
4
types/defaults.go
Normal file
4
types/defaults.go
Normal file
@@ -0,0 +1,4 @@
|
||||
package types
|
||||
|
||||
var Empty = NewType("", "")
|
||||
var Unknown = NewType("unknown", "")
|
||||
12
types/mime.go
Normal file
12
types/mime.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package types
|
||||
|
||||
type MIME struct {
|
||||
Type string
|
||||
Subtype string
|
||||
Value string
|
||||
}
|
||||
|
||||
func NewMIME(mime string) MIME {
|
||||
kind, subtype := splitMime(mime)
|
||||
return MIME{Type: kind, Subtype: subtype, Value: mime}
|
||||
}
|
||||
11
types/split.go
Normal file
11
types/split.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package types
|
||||
|
||||
import "strings"
|
||||
|
||||
func splitMime(s string) (string, string) {
|
||||
x := strings.Split(s, "/")
|
||||
if len(x) > 1 {
|
||||
return x[0], x[1]
|
||||
}
|
||||
return x[0], ""
|
||||
}
|
||||
14
types/type.go
Normal file
14
types/type.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package types
|
||||
|
||||
type Type struct {
|
||||
MIME MIME
|
||||
Extension string
|
||||
}
|
||||
|
||||
func NewType(ext, mime string) Type {
|
||||
t := Type{
|
||||
MIME: NewMIME(mime),
|
||||
Extension: ext,
|
||||
}
|
||||
return Add(t)
|
||||
}
|
||||
8
types/types.go
Normal file
8
types/types.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package types
|
||||
|
||||
var Types = make(map[string]Type)
|
||||
|
||||
func Add(t Type) Type {
|
||||
Types[t.Extension] = t
|
||||
return t
|
||||
}
|
||||
Reference in New Issue
Block a user