Skip to content

Commit

Permalink
Merge pull request #431 from mx1up/issue13_contenttype_to_ext
Browse files Browse the repository at this point in the history
[pkg-mime] contenttype to ext
  • Loading branch information
mosuem committed Sep 11, 2024
2 parents 79e3b8e + b5e6981 commit 56282a4
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 33 deletions.
5 changes: 5 additions & 0 deletions pkgs/mime/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down
4 changes: 4 additions & 0 deletions pkgs/mime/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
11 changes: 11 additions & 0 deletions pkgs/mime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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".
```
1 change: 1 addition & 0 deletions pkgs/mime/lib/mime.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
55 changes: 55 additions & 0 deletions pkgs/mime/lib/src/extension.dart
Original file line number Diff line number Diff line change
@@ -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<String, String> _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()];
14 changes: 0 additions & 14 deletions pkgs/mime/lib/src/mime_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,6 @@ int get defaultMagicNumbersMaxLength => _globalResolver.magicNumbersMaxLength;
String? lookupMimeType(String path, {List<int>? 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<String, String> _extensionMap = {};
Expand Down
2 changes: 1 addition & 1 deletion pkgs/mime/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
33 changes: 33 additions & 0 deletions pkgs/mime/test/extension_test.dart
Original file line number Diff line number Diff line change
@@ -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);
});
}
18 changes: 0 additions & 18 deletions pkgs/mime/test/mime_type_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
});
});
}

0 comments on commit 56282a4

Please sign in to comment.