Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provide better defaults for mimetype to extension mapping #81

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
86114bd
initial code from pr !15
mx1up Jan 5, 2023
9f1ef55
update copyright year
mx1up Jan 5, 2023
c9e3e15
improve lookup: case-insensitive and use firstWhereOrNull instead of …
mx1up Jan 6, 2023
0f37a4b
tests cleanup
mx1up Jan 6, 2023
f4c4a4f
check baseline
mx1up Jan 6, 2023
a9ec842
test to check whether each mimetype with multiple extensions has a pr…
mx1up Jan 6, 2023
02b9dcb
reformat
mx1up Jan 6, 2023
1ad29e9
apparently, the function already existed :roll-eyes: but the preferre…
mx1up Jan 6, 2023
84a8f43
probably better not to enforce a preferred extension for mime types w…
mx1up Jan 6, 2023
1f2ab54
added some preferred extensions for common file formats
mx1up Jan 6, 2023
fbf8f68
cleanup old tests
mx1up Jan 6, 2023
6600a3f
test orElse param for unknown mime type
mx1up Jan 16, 2023
6fe7ecb
link to file
mx1up Jan 16, 2023
d57b9fc
fix title to be more accurate
mx1up Jan 16, 2023
0d6c519
rename to existing function
mx1up Jan 16, 2023
bb9fc15
add standard behavior to make example more clear
mx1up Jan 16, 2023
7280615
include text/plain mapping again
mx1up Jan 16, 2023
40107ff
remove collection dependency
mx1up Jan 17, 2023
c384ef3
lookupExtension has been renamed to existing extensionFromMime
mx1up Jan 27, 2023
9ca2e5b
Merge branch 'dart-lang:master' into issue13_contenttype_to_ext
mx1up Dec 13, 2023
76f80f6
drop non-nullable extensionFromMime variant per review suggestion
mx1up Dec 13, 2023
7b2804f
doc update: try using noun phrase; don't refer to implementation details
mx1up Dec 13, 2023
65c7016
don't use extension method
mx1up Dec 13, 2023
e8a6ed5
remove addMimeType and leave it for another time..
mx1up Dec 13, 2023
f8642bd
remove addMimeType documentation
mx1up Dec 13, 2023
8acfbe4
be more specific about extension
mx1up Feb 19, 2024
f20c40f
review feedback: avoid abbreviations, just overwrite function param
mx1up Feb 19, 2024
2be331f
since extensions can no longer by added at runtime, we can optimize a…
mx1up Feb 19, 2024
60e9a7b
local var no longer needed
mx1up Feb 19, 2024
54da123
update docs
mx1up Feb 19, 2024
d190914
update docs
mx1up Feb 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
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 README.md
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')); // Will print html
print(extensionFromMime('image/jpeg')); // Will print jpg
print(extensionFromMime('application/pdf')); // Will print pdf
```
1 change: 1 addition & 0 deletions lib/mime.dart
Expand Up @@ -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';
36 changes: 36 additions & 0 deletions 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<String, String> _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()];
14 changes: 0 additions & 14 deletions lib/src/mime_type.dart
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
35 changes: 35 additions & 0 deletions 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);
});
});
}
17 changes: 0 additions & 17 deletions test/mime_type_test.dart
Expand Up @@ -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'));
});
});
}