# mime

> Look up media types by file extension and parse or build Content-Type values.

This is the Markdown version of https://osl-lang.org/packages/mime. Every page of the OSL docs has one: add .md to its path. https://osl-lang.org/llms.txt lists them all.

```osl
import "std:mime"
```

`std:mime` knows which media type (also called MIME type or content type) belongs to a file name, and how to read and write the `Content-Type` header value. Use it when you serve or upload files, decide how to handle a file by what it holds, or parse a header that comes with parameters such as `charset`.

## From file name to type

`mime.typeByFilename(path)` looks at the extension and ignores case. `mime.typeByExt(ext)` takes the extension alone, with or without the dot. Both return `null` when the extension isn't known, so use `??` for a fallback:

```osl
import "std:mime"

log mime.typeByFilename("photo.JPG")
log mime.typeByFilename("archive.tar.gz")
log mime.typeByExt(".json")
log mime.typeByExt("png")
log mime.typeByExt(".zzzz")

string type = mime.typeByFilename("data.unknown") ?? "application/octet-stream"
log type
```

Output:

```text
image/jpeg
application/gzip
application/json
image/png
null
application/octet-stream
```

Change the file name to `x.svg` or `x.wasm` and see what comes back.

`application/octet-stream` is the usual answer for "bytes of unknown kind". [serve](/packages/serve.md) already picks a `Content-Type` for files it serves, so you need `mime` when you set one yourself, such as on an upload.

## From type to extension

`mime.extByType` goes the other way, returning the usual extension with its dot, or `null`:

```osl
import "std:mime"

log mime.extByType("text/html")
log mime.extByType("image/png")
log mime.extByType("application/x-nothing")
```

Output:

```text
.html
.png
null
```

## Telling kinds of file apart

Five checks cover the common questions. Each exists in two forms: one that takes a media type, and one with `File` on the end that takes a file name:

```osl
import "std:mime"

log mime.isImage("image/png") mime.isAudio("audio/mpeg") mime.isVideo("video/mp4")
log mime.isText("application/json") mime.isText("image/png")
log mime.isImageFile("a.webp") mime.isTextFile("notes.md") mime.isVideoFile("x.txt")
```

Output:

```text
true true true
true false
true true false
```

`isText` counts anything readable as text, including JSON and XML, and not only `text/*`. That makes it a good test for "can I show this in a text box?".

## Parse a Content-Type

A `Content-Type` header holds a type and parameters. `mime.parse` returns the type lowercased and the parameters as an object, or an error for text that isn't a media type:

```osl
import "std:mime"

auto parsed = mime.parse("Text/HTML; charset=UTF-8; boundary=x").unwrap()
log parsed.type
log parsed.params["charset"]
log parsed.params

log mime.parse("???").isErr()
```

Output:

```text
text/html
UTF-8
{"boundary":"x","charset":"UTF-8"}
true
```

Parameter names are lowercased and their values are left as sent.

## Build a Content-Type

`mime.format(type, params)` writes the header value, quoting parameter values when they need it:

```osl
import "std:mime"

log mime.format("text/plain", {charset: "utf-8"})
log mime.format("multipart/form-data", {boundary: "----docs"})
```

Output:

```text
text/plain; charset=utf-8
multipart/form-data; boundary=----docs
```

**Quiz.** What does `mime.typeByFilename("data.unknown")` return?

- [ ] `"application/octet-stream"`
- [x] `null`
- [ ] An error.

Answer: An unknown extension has no type, so you choose the fallback with `??`.

### Exercise: Content-Type for a file

Write `typeFor(name)` that returns the media type of a file name, or `application/octet-stream` when it isn't known.

Starting code:

```osl
import "std:mime"

def typeFor(string name) string (
  return ""
)

log typeFor("logo.png")
log typeFor("notes.md")
log typeFor("blob.zzz")
```

It should print:

```text
image/png
text/markdown; charset=utf-8
application/octet-stream
```

Hint 1: `mime.typeByFilename(name)` returns `string?`.

Hint 2: `?? "application/octet-stream"` supplies the fallback.

A solution:

```osl
import "std:mime"

def typeFor(string name) string (
  return mime.typeByFilename(name) ?? "application/octet-stream"
)

log typeFor("logo.png")
log typeFor("notes.md")
log typeFor("blob.zzz")
```

## API reference

## API reference

Every member of `std:mime`, generated from the source of OSL.

### Functions

#### `mime.byFilename(any name) string`

**Deprecated.** Deprecated: use mime.typeByFilename(path) instead, which returns null for unknown extensions.

byFilename returns the media type for a file name, or "" when unknown.

#### `mime.extByType(string mediaType) string?`

extByType returns the conventional file extension for a media type, with
its leading dot (".png" for "image/png", ".jpg" for "image/jpeg"), or null
when the type is unknown. Parameters such as "; charset=utf-8" are ignored.

#### `mime.format(string mediaType, object params) string`

format builds a Content-Type value from a media type and its parameters,
quoting values where needed: mime.format("text/plain", {charset: "utf-8"})
is "text/plain; charset=utf-8". Throws ValueError when mediaType or a
parameter name is not a valid token.

#### `mime.isAudio(string mediaType) boolean`

isAudio reports whether a media type is an audio/* type. Case and
parameters are ignored. A value without "/" is checked as a file name or
extension ("clip.mp3", "mp3"), as before; for paths, use mime.isAudioFile.

#### `mime.isAudioFile(string path) boolean`

isAudioFile reports whether a file name or path has the extension of an
audio type. The file is not opened.

#### `mime.isImage(string mediaType) boolean`

isImage reports whether a media type is an image/* type. Case and
parameters are ignored. A value without "/" is checked as a file name or
extension ("cat.png", "png"), as before; for paths, use mime.isImageFile.

#### `mime.isImageFile(string path) boolean`

isImageFile reports whether a file name or path has the extension of an
image type. The file is not opened.

#### `mime.isText(string mediaType) boolean`

isText reports whether a media type holds text: text/*, JSON, XML,
JavaScript and the +json and +xml suffix types. Case and parameters are
ignored. A value without "/" is checked as a file name or extension
("notes.txt", "txt"), as before; for paths, use mime.isTextFile.

#### `mime.isTextFile(string path) boolean`

isTextFile reports whether a file name or path has the extension of a
text type, by the same rules as mime.isText. The file is not opened.

#### `mime.isVideo(string mediaType) boolean`

isVideo reports whether a media type is a video/* type. Case and
parameters are ignored. A value without "/" is checked as a file name or
extension ("movie.mp4", "mp4"), as before; for paths, use
mime.isVideoFile.

#### `mime.isVideoFile(string path) boolean`

isVideoFile reports whether a file name or path has the extension of a
video type. The file is not opened.

#### `mime.parse(string text) result<{params: string[string], type: string, valid: boolean (deprecated)}, error>`

parse splits a Content-Type value into its lowercased media type and its
parameters, returning {type, params} where params maps each parameter name
(lowercased) to its unquoted value; the deprecated key valid is always
true. Fails with ParseError when text is not a type/subtype pair or
repeats a parameter.

#### `mime.resolve(any input) string`

**Deprecated.** Deprecated: use mime.parse(text).type for a media type, or mime.typeByFilename(path) / mime.typeByExt(ext) for a file name.

resolve returns the bare lowercase media type of a media type string, or of
a file name or extension when input has no "/"; "" when unknown.

#### `mime.typeByExt(string ext) string?`

typeByExt returns the media type registered for a file extension, such as
"application/json" for "json" or ".json", or null when the extension is
unknown. Text types carry a charset parameter ("text/html; charset=utf-8").

#### `mime.typeByFilename(string path) string?`

typeByFilename returns the media type for a file name or path from its
extension, such as "image/png" for "photos/cat.png", or null when the name
has no known extension.
