brandembassy/file-type-detector
Detect file type and MIME by filename extension or by inspecting binary/stream content. Returns category, format, and MIME on success (or false). Includes a helper to resolve MIME only. Install via composer: brandembassy/file-type-detector.
Installation:
composer require brandembassy/file-type-detector
Add to composer.json if using a monorepo or custom package management.
First Use Case: Detect a file's MIME type from its filename (e.g., in a file upload handler):
use BrandEmbassy\FileTypeDetector\Detector;
$filename = 'example.pdf';
$result = Detector::detectByFilename($filename);
if ($result) {
$mimeType = $result[2]; // e.g., 'application/pdf'
// Use $mimeType in Laravel (e.g., for Storage::put())
}
Key Classes:
Detector: Core class for detection methods.FileInfo: Object returned by newer methods (e.g., detectFromContent()), encapsulating type, format, and MIME type.use Illuminate\Http\Request;
use BrandEmbassy\FileTypeDetector\Detector;
public function upload(Request $request)
{
$file = $request->file('document');
$stream = fopen($file->getPathname(), 'r');
$fileInfo = Detector::detectFromContent($stream);
fclose($stream);
if ($fileInfo->getMimeType() === 'image/jpeg') {
// Process as JPEG
}
}
use Symfony\Component\HttpFoundation\BinaryFileResponse;
public function serveFile($path)
{
$mime = Detector::getMimeType($path);
return response()->file($path, [
'Content-Type' => $mime,
]);
}
$extension = pathinfo($filename, PATHINFO_EXTENSION);
$mime = Detector::resolveMimeTypeFromExtension($extension);
$stream = fopen('http://example.com/file.pdf', 'r');
$fileInfo = Detector::detectFromContent($stream);
// Handle stream without seeking (supports non-seekable resources)
$byFilename = Detector::detectByFilename($filename);
$byContent = Detector::detectByContent($filePath);
$result = $byContent ?: $byFilename;
Service Provider Binding:
Bind Detector as a singleton in AppServiceProvider for dependency injection:
public function register()
{
$this->app->singleton(Detector::class);
}
Form Request Validation:
Use the package to validate file types in FormRequest classes:
public function rules()
{
return [
'file' => 'required|file',
];
}
public function withValidator($validator)
{
$file = $this->file;
$mime = Detector::getMimeType($file->getPathname());
$validator->after(function ($validator) use ($mime) {
if (!in_array($mime, ['image/jpeg', 'image/png'])) {
$validator->errors()->add('file', 'Invalid file type.');
}
});
}
Storage Disk Integration:
Use MIME types to optimize storage (e.g., public disk for web-accessible files):
$mime = Detector::getMimeType($filePath);
$path = "uploads/{$mime}/{$file->hashName()}";
Storage::disk('public')->put($path, file_get_contents($filePath));
Cache Results: Cache MIME type detections for frequently accessed files (e.g., using Laravel's cache):
$cacheKey = "mime:{$filePath}";
$mime = Cache::remember($cacheKey, now()->addHours(1), function () use ($filePath) {
return Detector::getMimeType($filePath);
});
Batch Processing:
For bulk operations (e.g., migrating files), process files in chunks and reuse Detector instances.
detectByContent() may fail on non-seekable streams (e.g., HTTP streams) if not using detectFromContent().Detector::detectFromContent($stream) for streams that cannot be rewound..jpg.txt). Content detection is more reliable but slower.$byContent = Detector::detectByContent($filePath);
$byFilename = Detector::detectByFilename($filePath);
$result = $byContent ?: $byFilename;
.JPG vs .jpg).$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$stream = fopen($filePath, 'r');
$buffer = fread($stream, 1024); // Read first 1KB
$fileInfo = Detector::detectFromContent($buffer);
fclose($stream);
$result = Detector::detectByContent($filePath);
logger()->debug('Detection result', [
'filename' => $filePath,
'result' => $result,
'mime' => $result ? $result[2] : 'unknown',
]);
filesize() or md5_file() to verify.Custom Signatures: Extend detection by adding new file signatures. Override the Detector class or use composition:
class CustomDetector extends Detector
{
protected function getSignatures(): array
{
return array_merge(
parent::getSignatures(),
[
'CUSTOM_FORMAT' => [
'signatures' => ['\x00CUSTOM'],
'mime' => 'application/x-custom',
],
]
);
}
}
Custom MIME Types: Add mappings in Detector::getMimeTypes():
protected function getMimeTypes(): array
{
return array_merge(
parent::getMimeTypes(),
['application/x-custom' => 'custom']
);
}
rector to upgrade if needed.finfo (e.g., symfony/mime).Unit Tests: Mock Detector in tests:
$mockDetector = Mockery::mock(Detector::class);
$mockDetector->shouldReceive('detectByFilename')
->with('test.pdf')
->andReturn(['PDF', 'PDF', 'application/pdf']);
$this->app->instance(Detector::class, $mockDetector);
Test Data: Use a mix of:
.pdf, .jpgHow can I help you explore Laravel packages today?