diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f5e0ea..463063a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,3 +31,7 @@ All files in the project must start with the following header. Contributions made by corporations are covered by a different agreement than the one above, the [Software Grant and Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate). + +### Adding an extension / MIME type mapping +If a MIME type ends up with multiple extensions, it is recommended to define a +preferred extension in `_defaultMimeTypeMap` in [extension.dart](lib/src/extension.dart). \ No newline at end of file diff --git a/README.md b/README.md index 4cd060c..7ea762c 100644 --- a/README.md +++ b/README.md @@ -57,3 +57,14 @@ request .map((part) => part.fold(0, (p, d) => p + d)) .listen((length) => print('Part with length $length')); ``` + +## Determining file extension by MIME type + +The top level function `extensionFromMime` can be used to determine the +file extension of a given MIME type. + +```dart +print(extensionFromMime('text/html')); // Will print html +print(extensionFromMime('image/jpeg')); // Will print jpg +print(extensionFromMime('application/pdf')); // Will print pdf +``` diff --git a/lib/mime.dart b/lib/mime.dart index 12df281..1b452a2 100644 --- a/lib/mime.dart +++ b/lib/mime.dart @@ -9,6 +9,7 @@ /// [Internet media type](http://en.wikipedia.org/wiki/Internet_media_type). library mime; +export 'src/extension.dart'; export 'src/mime_multipart_transformer.dart'; export 'src/mime_shared.dart'; export 'src/mime_type.dart'; diff --git a/lib/src/extension.dart b/lib/src/extension.dart new file mode 100644 index 0000000..35a860f --- /dev/null +++ b/lib/src/extension.dart @@ -0,0 +1,36 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'default_extension_map.dart'; + +/// Reverse map of [defaultExtensionMap] with overrides for common extensions +/// since different extensions may map to the same MIME type. +final Map _defaultMimeTypeMap = { + for (var entry in defaultExtensionMap.entries) entry.value: entry.key, +}..addAll({ + 'application/vnd.ms-excel': 'xls', + 'application/vnd.ms-powerpoint': 'ppt', + 'image/jpeg': 'jpg', + 'image/tiff': 'tif', + 'image/svg+xml': 'svg', + 'text/calendar': 'ics', + 'text/javascript': 'js', + 'text/plain': 'txt', + 'text/sgml': 'sgml', + 'text/x-pascal': 'pas', + 'video/mp4': 'mp4', + 'video/mpeg': 'mpg', + 'video/quicktime': 'mov', + 'video/x-matroska': 'mkv', + }); + +/// The file extension for a given MIME type. +/// +/// If there are multiple extensions for [mimeType], return preferred extension +/// if defined in [_defaultMimeTypeMap], otherwise an extension chosen by the +/// library. +/// +/// If no extension is found, `null` is returned. +String? extensionFromMime(String mimeType) => + _defaultMimeTypeMap[mimeType.toLowerCase()]; diff --git a/lib/src/mime_type.dart b/lib/src/mime_type.dart index 03121c9..9cf9194 100644 --- a/lib/src/mime_type.dart +++ b/lib/src/mime_type.dart @@ -23,20 +23,6 @@ int get defaultMagicNumbersMaxLength => _globalResolver.magicNumbersMaxLength; String? lookupMimeType(String path, {List? headerBytes}) => _globalResolver.lookup(path, headerBytes: headerBytes); -/// Returns the extension for the given MIME type. -/// -/// If there are multiple extensions for [mime], return the first occurrence in -/// the map. If there are no extensions for [mime], return [mime]. -String extensionFromMime(String mime) { - mime = mime.toLowerCase(); - for (final entry in defaultExtensionMap.entries) { - if (defaultExtensionMap[entry.key] == mime) { - return entry.key; - } - } - return mime; -} - /// MIME-type resolver class, used to customize the lookup of mime-types. class MimeTypeResolver { final Map _extensionMap = {}; diff --git a/test/extension_test.dart b/test/extension_test.dart new file mode 100644 index 0000000..0a3db0f --- /dev/null +++ b/test/extension_test.dart @@ -0,0 +1,35 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:mime/mime.dart'; +import 'package:test/test.dart'; + +void main() { + group('global-lookup-mime-type', () { + test('valid-mime-type', () { + expect(extensionFromMime('text/x-dart'), equals('dart')); + expect(extensionFromMime('text/javascript'), equals('js')); + expect(extensionFromMime('application/java-archive'), equals('jar')); + expect(extensionFromMime('application/json'), equals('json')); + expect(extensionFromMime('application/pdf'), equals('pdf')); + expect(extensionFromMime('application/vnd.ms-excel'), equals('xls')); + expect(extensionFromMime('application/xhtml+xml'), equals('xht')); + expect(extensionFromMime('image/jpeg'), equals('jpg')); + expect(extensionFromMime('image/png'), equals('png')); + expect(extensionFromMime('text/css'), equals('css')); + expect(extensionFromMime('text/html'), equals('htm')); + expect(extensionFromMime('text/plain'), equals('txt')); + expect(extensionFromMime('text/x-c'), equals('c')); + }); + + test('invalid-mime-type', () { + expect(extensionFromMime('invalid-mime-type'), isNull); + expect(extensionFromMime('invalid/mime/type'), isNull); + }); + + test('unknown-mime-type', () { + expect(extensionFromMime('application/to-be-invented'), isNull); + }); + }); +} diff --git a/test/mime_type_test.dart b/test/mime_type_test.dart index f7388ff..56efba8 100644 --- a/test/mime_type_test.dart +++ b/test/mime_type_test.dart @@ -318,21 +318,4 @@ void main() { expect(initialMagicNumbersMaxLength, actualMaxBytes); }); - group('extensionFromMime', () { - test('returns match for mime with single extension', () { - expect(extensionFromMime('application/json'), equals('json')); - expect(extensionFromMime('application/java-archive'), equals('jar')); - }); - - test('returns first match for mime with multiple extensions', () { - expect(extensionFromMime('text/html'), equals('htm')); - expect(extensionFromMime('application/x-cbr'), equals('cb7')); - }); - - test('returns inputted string for unrecognized mime', () { - expect( - extensionFromMime('unrecognized_mime'), equals('unrecognized_mime')); - expect(extensionFromMime('i/am/not/a/mime'), equals('i/am/not/a/mime')); - }); - }); }