diff --git a/pkgs/mime/CHANGELOG.md b/pkgs/mime/CHANGELOG.md index 50f243306..55332bac0 100644 --- a/pkgs/mime/CHANGELOG.md +++ b/pkgs/mime/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.0-wip + +* **[Breaking]** `extensionFromMime(String mimeType)` returns `null` instead of `mimeType` for an unknown mime type. +* Update `extensionFromMime` to return a default extension when a MIME type maps to multiple extensions. + ## 1.0.6 * Add `topics` section to `pubspec.yaml`. diff --git a/pkgs/mime/CONTRIBUTING.md b/pkgs/mime/CONTRIBUTING.md index 6f5e0ea67..463063ae4 100644 --- a/pkgs/mime/CONTRIBUTING.md +++ b/pkgs/mime/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/pkgs/mime/README.md b/pkgs/mime/README.md index 8a814150c..da06de62b 100644 --- a/pkgs/mime/README.md +++ b/pkgs/mime/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')); // Prints "html". +print(extensionFromMime('image/jpeg')); // Prints "jpg". +print(extensionFromMime('application/pdf')); // Prints "pdf". +``` diff --git a/pkgs/mime/lib/mime.dart b/pkgs/mime/lib/mime.dart index f2c446403..cb450981f 100644 --- a/pkgs/mime/lib/mime.dart +++ b/pkgs/mime/lib/mime.dart @@ -9,6 +9,7 @@ /// [Internet media type](http://en.wikipedia.org/wiki/Internet_media_type). library; +export 'src/extension.dart'; export 'src/mime_multipart_transformer.dart'; export 'src/mime_shared.dart'; export 'src/mime_type.dart'; diff --git a/pkgs/mime/lib/src/extension.dart b/pkgs/mime/lib/src/extension.dart new file mode 100644 index 000000000..293449a0e --- /dev/null +++ b/pkgs/mime/lib/src/extension.dart @@ -0,0 +1,55 @@ +// 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'; + +/// Default extension for recognized MIME types. +/// +/// Is the inverse of [defaultExtensionMap], and where that +/// map has multiple extensions which map to the same +/// MIME type, this map maps that MIME type to a *default* +/// extension. +/// +/// Used by [extensionFromMime]. +final Map _defaultMimeTypeMap = { + for (var entry in defaultExtensionMap.entries) entry.value: entry.key, + 'application/msword': 'doc', + 'application/vnd.ms-excel': 'xls', + 'application/vnd.ms-powerpoint': 'ppt', + 'application/x-debian-package': 'deb', + 'application/xhtml+xml': 'xhtml', + 'application/xml': 'xml', + 'audio/x-aiff': 'aif', + 'audio/midi': 'mid', + 'audio/mp4': 'm4a', + 'audio/ogg': 'ogg', + 'image/jpeg': 'jpg', + 'image/tiff': 'tif', + 'image/svg+xml': 'svg', + 'model/vrml': 'vrml', + 'text/calendar': 'ics', + 'text/html': 'html', + 'text/javascript': 'js', + 'text/markdown': 'md', + 'text/plain': 'txt', + 'text/sgml': 'sgml', + 'text/x-asm': 'asm', + 'text/x-c': 'c', + 'text/x-pascal': 'pas', + 'video/mp4': 'mp4', + 'video/mpeg': 'mpg', + 'video/quicktime': 'mov', + 'video/x-matroska': 'mkv', +}; + +/// The default file extension for a given MIME type. +/// +/// If [mimeType] has multiple associated extensions, +/// the returned string is one of those, chosen as the default +/// extension for that MIME type. +/// +/// Returns `null` if [mimeType] is not a recognized and +/// supported MIME type. +String? extensionFromMime(String mimeType) => + _defaultMimeTypeMap[mimeType.toLowerCase()]; diff --git a/pkgs/mime/lib/src/mime_type.dart b/pkgs/mime/lib/src/mime_type.dart index 03121c97b..9cf9194a1 100644 --- a/pkgs/mime/lib/src/mime_type.dart +++ b/pkgs/mime/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/pkgs/mime/pubspec.yaml b/pkgs/mime/pubspec.yaml index 8d3c49823..2cfa75a33 100644 --- a/pkgs/mime/pubspec.yaml +++ b/pkgs/mime/pubspec.yaml @@ -1,5 +1,5 @@ name: mime -version: 1.0.6 +version: 2.0.0-wip description: >- Utilities for handling media (MIME) types, including determining a type from a file extension and file contents. diff --git a/pkgs/mime/test/extension_test.dart b/pkgs/mime/test/extension_test.dart new file mode 100644 index 000000000..40dae6df5 --- /dev/null +++ b/pkgs/mime/test/extension_test.dart @@ -0,0 +1,33 @@ +// 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() { + 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('xhtml')); + expect(extensionFromMime('image/jpeg'), equals('jpg')); + expect(extensionFromMime('image/png'), equals('png')); + expect(extensionFromMime('text/css'), equals('css')); + expect(extensionFromMime('text/html'), equals('html')); + 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/pkgs/mime/test/mime_type_test.dart b/pkgs/mime/test/mime_type_test.dart index f7388ff34..23cb34af3 100644 --- a/pkgs/mime/test/mime_type_test.dart +++ b/pkgs/mime/test/mime_type_test.dart @@ -317,22 +317,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')); - }); - }); }