# `Storehouse.File`
[🔗](https://github.com/hattoriai/storehouse/blob/v0.2.0/lib/storehouse/file.ex#L1)

A stored file: what `Storehouse.store/2` gives, and what an app keeps in
its records (or a document's data) to show and serve it again.

  * `:key` - its key in the store (`Storehouse.key/2`).
  * `:url` - its URL, from the store.
  * `:name` - the client's file name, for display only
    (`Storehouse.clean_name/1`).
  * `:content_type` - the type its bytes prove (`Storehouse.Check`).
  * `:bytes` - its size.
  * `:width` and `:height` - an image's, as a browser draws it (turned as
    a JPEG's EXIF says), or `nil`.
  * `:checksum` - the SHA-256 of its bytes, in Base64 (as S3's
    `x-amz-checksum-sha256`): the store checked it as it took the file,
    and `Storehouse.checksum/1` gives it of a copy, to compare. `nil` for
    a file uploaded straight to a store that keeps none
    (`Storehouse.verify_upload/2`).
  * `:preview` - the URL of a smaller image of it (a thumbnail, a PDF's
    first page, a video's poster), when the app made one, or `nil`.
  * `:metadata` - the app's own (`Storehouse.Metadata`): a caption, a
    credit. `%{}` when there is none.
  * `:analysis` - what the analysis steps made of the file
    (`Storehouse.Analyzer`), by step name, each a `Storehouse.Analysis`.
    `%{}` when none ran.

## In JSON

A file encodes as JSON (`JSON.Encoder` on Elixir 1.18 and later, and
`Jason.Encoder` when Jason is there) with its fields and metadata, but
not its analysis: what a file's EXIF says can hold where a photo was
taken. `to_map/2` chooses what goes.

# `t`

```elixir
@type t() :: %Storehouse.File{
  analysis: %{required(String.t()) =&gt; Storehouse.Analysis.t()},
  bytes: non_neg_integer(),
  checksum: String.t() | nil,
  content_type: String.t(),
  height: pos_integer() | nil,
  key: Storehouse.Adapter.key(),
  metadata: map(),
  name: String.t(),
  preview: String.t() | nil,
  url: String.t(),
  width: pos_integer() | nil
}
```

# `analysis`

```elixir
@spec analysis(t(), String.t() | atom()) :: Storehouse.Analysis.t() | nil
```

What the analysis step `step` made of the file (`Storehouse.Analysis`),
or `nil` when it has not run.

# `analyzed`

```elixir
@spec analyzed(t(), String.t() | atom()) :: map() | nil
```

What the analysis step `step` read from the file, when it ran and did
not fail or skip it: its result, else `nil`.

## Examples

    iex> file = %Storehouse.File{key: "a.pdf", url: "/a.pdf", name: "a.pdf", content_type: "application/pdf", bytes: 1}
    iex> file = Storehouse.File.put_analysis(file, "pages", Storehouse.Analysis.ok(%{"pages" => 12}))
    iex> Storehouse.File.analyzed(file, :pages)
    %{"pages" => 12}

# `image?`

```elixir
@spec image?(t()) :: boolean()
```

Whether the file is an image (PNG, JPEG, GIF or WebP).

# `put_metadata`

```elixir
@spec put_metadata(t(), map()) :: {:ok, t()} | {:error, {:invalid_metadata, list()}}
```

Merges `metadata` into the file's own (`Storehouse.Metadata.merge/2`):
a caption a person entered, a credit. It is normalized first
(`Storehouse.Metadata.normalize/1`), and refused with
`{:error, {:invalid_metadata, path}}` when it holds what JSON does not.

## Examples

    iex> file = %Storehouse.File{key: "a.png", url: "/a.png", name: "a.png", content_type: "image/png", bytes: 1}
    iex> {:ok, file} = Storehouse.File.put_metadata(file, %{caption: "The quay"})
    iex> {:ok, file} = Storehouse.File.put_metadata(file, %{credit: "Ana"})
    iex> file.metadata
    %{"caption" => "The quay", "credit" => "Ana"}

# `to_map`

```elixir
@spec to_map(t(), keyword()) :: map()
```

The file as a map with string keys, for JSON: its fields and metadata,
without its analysis unless asked.

  * `:only` - the fields to give, of `[:key, :url, :name, :content_type, :bytes, :width, :height, :checksum, :preview, :metadata]`. The default
    is all of them.
  * `:analysis` - the analysis steps to give, under `"analysis"`: a list
    of their names, or `true` for every one. The default is none.

## Examples

    iex> file = %Storehouse.File{key: "a.png", url: "/a.png", name: "a.png", content_type: "image/png", bytes: 1, metadata: %{"caption" => "Hi"}}
    iex> Storehouse.File.to_map(file, only: [:url, :metadata])
    %{"url" => "/a.png", "metadata" => %{"caption" => "Hi"}}

---

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