Skip to content

Commit 9f52857

Browse files
authored
Merge pull request #18 from vicajilau/bugfix/jpeg-magic-number-issue
Fixed JPEG Magic Number
2 parents 78401f6 + e0cb814 commit 9f52857

8 files changed

+94
-12
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 1.4.0
2+
* Fixed an issue where JPEG files were detected as TIFF files [#17](https://github.com/vicajilau/file_magic_number/issues/17).
3+
* Added webp support.
4+
* Added AVI support.
5+
* Improved WAV support.
6+
* Added magic number detection byRange.
7+
18
## 1.3.2
29
* Fixed an issue where PDFA files were not detected.
310
* Removed Cocoapods on example project.

example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ packages:
8787
path: ".."
8888
relative: true
8989
source: path
90-
version: "1.3.2"
90+
version: "1.4.0"
9191
file_picker:
9292
dependency: "direct main"
9393
description:

lib/src/file_magic_number.dart

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:typed_data';
33
import 'package:file_magic_number/src/magic_number_list.dart';
44

55
import '../file_magic_number.dart';
6+
import 'file_magic_number_match_type.dart';
67

78
/// A utility class for detecting file types based on their magic numbers.
89
///
@@ -27,13 +28,46 @@ class FileMagicNumber {
2728
}
2829

2930
for (var entry in MagicNumberList.magicNumbers.entries) {
30-
if (_matchesWithOffset(bytes, entry.key)) {
31-
return entry.value;
31+
switch (FileMagicNumberMatchType.get(entry.value)) {
32+
case FileMagicNumberMatchType.exact:
33+
if (_matchAt(bytes, entry.key, 0)) {
34+
return entry.value;
35+
}
36+
case FileMagicNumberMatchType.offset:
37+
if (_matchesWithOffset(bytes, entry.key)) {
38+
return entry.value;
39+
}
40+
case FileMagicNumberMatchType.byRange:
41+
final value = _detectRiffBasedFormat(bytes);
42+
if (value != null) return value;
3243
}
3344
}
3445
return FileMagicNumberType.unknown;
3546
}
3647

48+
static FileMagicNumberType? _detectRiffBasedFormat(Uint8List bytes) {
49+
if (bytes.length < 12) return null;
50+
51+
final isRIFF = bytes[0] == 0x52 &&
52+
bytes[1] == 0x49 &&
53+
bytes[2] == 0x46 &&
54+
bytes[3] == 0x46;
55+
56+
if (!isRIFF) return null;
57+
58+
final format = String.fromCharCodes(bytes.sublist(8, 12));
59+
switch (format) {
60+
case 'WEBP':
61+
return FileMagicNumberType.webp;
62+
case 'WAVE':
63+
return FileMagicNumberType.wav;
64+
case 'AVI ':
65+
return FileMagicNumberType.avi;
66+
default:
67+
return null;
68+
}
69+
}
70+
3771
/// Detects the file type from a byte array using its magic number.
3872
///
3973
/// - [bytes]: The byte data of the file.
@@ -88,7 +122,7 @@ class FileMagicNumber {
88122
/// Check whether the byte sequence (representing a file signature) exists within the data stream.
89123
static bool _matchAt(Uint8List data, List<int> pattern, int offset) {
90124
for (int i = 0; i < pattern.length; i++) {
91-
if (data[offset + i] != pattern[i]) {
125+
if (offset + i < data.length && data[offset + i] != pattern[i]) {
92126
return false;
93127
}
94128
}

lib/src/file_magic_number_match_type.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import '../file_magic_number.dart';
77
/// or if it can appear at any position within a predefined range (`offset`).
88
enum FileMagicNumberMatchType {
99
exact,
10-
offset;
10+
offset,
11+
byRange;
1112

1213
/// Determines how the magic number should be matched for this file type.
1314
///
@@ -16,13 +17,18 @@ enum FileMagicNumberMatchType {
1617
/// offset, so they are matched differently (`offset`).
1718
///
1819
/// This method is used internally by the library during file type detection.
19-
static bool isExact(FileMagicNumberType type) {
20+
static FileMagicNumberMatchType get(FileMagicNumberType type) {
2021
switch (type) {
2122
case FileMagicNumberType.mp4:
2223
case FileMagicNumberType.heic:
23-
return false;
24+
case FileMagicNumberType.pdf:
25+
return offset;
26+
case FileMagicNumberType.webp:
27+
case FileMagicNumberType.wav:
28+
case FileMagicNumberType.avi:
29+
return byRange;
2430
default:
25-
return true;
31+
return exact;
2632
}
2733
}
2834
}

lib/src/file_magic_number_type.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ enum FileMagicNumberType {
5353
/// MP4 (MPEG-4 Part 14) video file format.
5454
mp4,
5555

56+
/// WebP (WebP)
57+
webp,
58+
59+
/// AVI video file format.
60+
avi,
61+
5662
// Other formats
5763
/// PDF (Portable Document Format) file.
5864
pdf,

lib/src/magic_number_list.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ class MagicNumberList {
3636
[0x7F, 0x45, 0x4C, 0x46]: FileMagicNumberType.elf,
3737
[0x52, 0x49, 0x46, 0x46]: FileMagicNumberType.wav,
3838
[0x50, 0x4B, 0x03, 0x04]: FileMagicNumberType.zip,
39+
[0xFF, 0xD8, 0xFF, 0xE0]: FileMagicNumberType.jpg, // JPEG (JFIF)
40+
[0xFF, 0xD8, 0xFF, 0xE1]: FileMagicNumberType.jpg, // JPEG (EXIF)
3941

4042
// 3 bytes
4143
[0x49, 0x44, 0x33]: FileMagicNumberType.mp3,
42-
[0xFF, 0xD8, 0xFF]: FileMagicNumberType.jpg,
44+
[0xFF, 0xD8, 0xFF]: FileMagicNumberType.jpg, // JPEG (generic)
4345

4446
// 2 bytes
4547
[0x4D, 0x5A]: FileMagicNumberType.exe,

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: file_magic_number
22
description: "A Flutter package to detect file types based on their magic number instead of MIME types. Supports Flutter on mobile, desktop, and web without native code."
3-
version: 1.3.2
3+
version: 1.4.0
44
homepage: https://github.com/vicajilau/file_magic_number
55

66
environment:

test/file_magic_number_bytes_test.dart

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ void main() {
2626
});
2727

2828
test('Detects RAR v5 file', () {
29-
final bytes = Uint8List.fromList([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07]);
29+
final bytes = Uint8List.fromList([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00]);
3030
final result = FileMagicNumber.detectFileTypeFromBytes(bytes);
3131
expect(result, FileMagicNumberType.rar);
3232
});
@@ -80,11 +80,38 @@ void main() {
8080
});
8181

8282
test('Detects WAV file', () {
83-
final bytes = Uint8List.fromList([0x52, 0x49, 0x46, 0x46]);
83+
// RIFF....WAVE (offset 0: "RIFF", offset 8: "WAVE")
84+
final bytes = Uint8List.fromList([
85+
0x52, 0x49, 0x46, 0x46, // "RIFF"
86+
0x00, 0x00, 0x00, 0x00, // dummy size
87+
0x57, 0x41, 0x56, 0x45 // "WAVE"
88+
]);
8489
final result = FileMagicNumber.detectFileTypeFromBytes(bytes);
8590
expect(result, FileMagicNumberType.wav);
8691
});
8792

93+
test('Detects WebP file', () {
94+
// RIFF....WEBP (offset 0: "RIFF", offset 8: "WEBP")
95+
final bytes = Uint8List.fromList([
96+
0x52, 0x49, 0x46, 0x46, // "RIFF"
97+
0x00, 0x00, 0x00, 0x00, // dummy size
98+
0x57, 0x45, 0x42, 0x50 // "WEBP"
99+
]);
100+
final result = FileMagicNumber.detectFileTypeFromBytes(bytes);
101+
expect(result, FileMagicNumberType.webp);
102+
});
103+
104+
test('Detects AVI file', () {
105+
// RIFF....AVI (offset 0: "RIFF", offset 8: "AVI ")
106+
final bytes = Uint8List.fromList([
107+
0x52, 0x49, 0x46, 0x46, // "RIFF"
108+
0x00, 0x00, 0x00, 0x00, // dummy size
109+
0x41, 0x56, 0x49, 0x20 // "AVI "
110+
]);
111+
final result = FileMagicNumber.detectFileTypeFromBytes(bytes);
112+
expect(result, FileMagicNumberType.avi);
113+
});
114+
88115
test('Detects MP4 file', () {
89116
final bytes =
90117
Uint8List.fromList([0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6F, 0x6D]);

0 commit comments

Comments
 (0)