# `PhoenixKit.Utils.FileHash`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v3.5.0/lib/phoenix_kit/utils/file_hash.ex#L1)

Checksums of a file on disk, read in chunks.

A video can be gigabytes. `File.read!/1` followed by `:crypto.hash/2` holds the
whole file in memory (twice, while it is copied into a binary), once for every
checksum taken: the original's SHA-256 and MD5 at upload, each rendition's, the
dedup backfill. This reads the file a megabyte at a time, so what a hash costs in
memory is the chunk, whatever the file's size.

    FileHash.sha256("/tmp/video.mov")
    #=> {:ok, "9f86d0…"}

    FileHash.hashes("/tmp/video.mov", [:sha256, :md5])
    #=> {:ok, %{sha256: "…", md5: "…"}}   # one pass over the file

Digests are lowercase hexadecimal, as everywhere in storage. The `!` forms raise
`File.Error` as `File.read!/1` did, for the callers that were written around it.

# `algorithm`

```elixir
@type algorithm() :: :sha256 | :md5 | :sha | :sha384 | :sha512
```

# `result`

```elixir
@type result() :: {:ok, String.t()} | {:error, File.posix() | :badarg}
```

# `hashes`

```elixir
@spec hashes(Path.t(), [algorithm()], keyword()) ::
  {:ok, %{required(algorithm()) =&gt; String.t()}}
  | {:error, File.posix() | :badarg}
```

Several digests of the file in one pass over it.

Returns `{:ok, %{algorithm => hex}}` for the algorithms asked for, or
`{:error, :badarg}` when one is not an `t:algorithm/0`. `opts`: `:chunk_size` (bytes per read; default 1 MiB).

# `md5`

```elixir
@spec md5(Path.t(), keyword()) :: result()
```

The MD5 of the file at `path`.

# `md5!`

```elixir
@spec md5!(Path.t(), keyword()) :: String.t()
```

`md5/2`, raising `File.Error` when the file cannot be read.

# `sha256`

```elixir
@spec sha256(Path.t(), keyword()) :: result()
```

The SHA-256 of the file at `path`.

# `sha256!`

```elixir
@spec sha256!(Path.t(), keyword()) :: String.t()
```

`sha256/2`, raising `File.Error` when the file cannot be read.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
