# `ExOss.Storage`
[🔗](https://github.com/choice-form/ex_oss/blob/main/lib/ex_oss/storage.ex#L1)

S3-based storage implementation for AWS S3 and S3-compatible services.

Supports three provider modes:

  * `:aws` — Standard AWS S3 (endpoint auto-constructed as `s3.{region}.amazonaws.com`)
  * `:qiniu` — Qiniu S3-compatible API (endpoint auto-constructed as `s3.{region}.qiniucs.com`)
  * `:custom` — Any S3-compatible service (MinIO, DigitalOcean Spaces, Cloudflare R2, etc.)
    Requires an explicit `endpoint` to be configured.

All storage operations go through `ex_aws_s3`.

## File Transfer Operations

Use `run/2` with an `ExOss.StorageTask` struct to perform uploads,
copies, deletes, and content uploads:

    # Upload from local file
    ExOss.Storage.run(client, %ExOss.StorageTask{
      source: {:file, "/path/to/file.jpg"},
      target: {:cloud, "bucket", "uploads/file.jpg"}
    })

    # Upload from remote URL
    ExOss.Storage.run(client, %ExOss.StorageTask{
      source: {:remote, "https://example.com/file.jpg"},
      target: {:cloud, "bucket", "uploads/file.jpg"}
    })

    # Cloud-to-cloud copy
    ExOss.Storage.run(client, %ExOss.StorageTask{
      source: {:cloud, "source-bucket", "file.jpg"},
      target: {:cloud, "target-bucket", "copies/file.jpg"}
    })

    # Delete immediately
    ExOss.Storage.run(client, %ExOss.StorageTask{
      source: {:cloud, "bucket", "file.jpg"},
      target: nil
    })

    # Delete after N days (via lifecycle tags)
    ExOss.Storage.run(client, %ExOss.StorageTask{
      source: {:cloud, "bucket", "file.jpg"},
      target: nil,
      opts: [days: 30]
    })

## Presigned URLs

Generate temporary, direct-access URLs without CDN:

    # Download URL (presigned GET)
    ExOss.Storage.authorize_download_url(client, "bucket", "file.jpg", 3600)

    # Upload URL (presigned PUT)
    ExOss.Storage.upload_credential(client, "bucket", "file.jpg", 3600)

When a CDN is configured on the client, use `ExOss.Runner` instead —
it automatically routes to the CDN adapter when `cdn_download` /
`cdn_upload` are enabled.

# `attach_endpoint`

```elixir
@spec attach_endpoint(ExOss.Client.Client.t(), binary()) :: ExOss.Client.Client.t()
```

Resolves and sets the endpoint URL on the client based on the provider.

For `:aws` and `:qiniu`, the endpoint is auto-constructed as
`https://{bucket}.s3.{region}.amazonaws.com` (or `qiniucs.com`).

For `:custom`, the endpoint must already be set on the client.

# `authorize_download_url`

```elixir
@spec authorize_download_url(
  ExOss.Client.Client.t(),
  binary(),
  binary(),
  non_neg_integer(),
  keyword()
) ::
  binary()
```

Generates an S3 presigned download URL for the given resource key.

## Options

  * `:query_params` — additional query parameters to include in the signed URL
  * `:headers` — headers to include in the signature
  * `:att_name` — (not used by S3, handled by CDN adapters)

## Examples

    iex> ExOss.Storage.authorize_download_url(client, "bucket", "file.jpg", 3600, [])
    "https://bucket.s3.us-east-1.amazonaws.com/file.jpg?X-Amz-..."

# `metadata`

```elixir
@spec metadata(ExOss.Client.Client.t(), binary(), binary()) ::
  {:ok, %{file_size: non_neg_integer(), mime_type: binary()}}
  | {:error, :no_metadata}
```

Retrieves metadata for a stored object via a HEAD request.

Returns `{:ok, %{file_size: integer, mime_type: string}}` on success,
or `{:error, :no_metadata}` if the object does not exist or is not
accessible.

# `mkzip_index_content`

```elixir
@spec mkzip_index_content([%{access_address: binary(), alias_name: binary()}]) ::
  binary()
```

Generates zip index content for local zip operations.

Each item must contain `:access_address` (the source URL) and
`:alias_name` (the filename inside the zip). Both are Base64
url-safe encoded into the index format expected by
`ExOss.LocalZipper.zip/1`.

## Examples

    iex> ExOss.Storage.mkzip_index_content([
    ...>   %{access_address: "https://s3.example.com/1.jpg", alias_name: "img1.jpg"}
    ...> ])
    "/url/aHR0cHM6Ly9zMy5leGFtcGxlLmNvbS8xLmpwZw==/alias/aW1nMS5qcGc="

# `run`

# `upload_credential`

```elixir
@spec upload_credential(
  ExOss.Client.Client.t(),
  binary(),
  binary(),
  non_neg_integer()
) ::
  ExOss.UploadCredential.t()
```

Generates an S3 presigned upload URL for the given resource key.

Returns an `ExOss.UploadCredential` struct with the `presigned_url` field
set to a PUT presigned URL. The `uptoken` field is `nil` for S3.

## Examples

    iex> cred = ExOss.Storage.upload_credential(client, "bucket", "file.jpg", 3600)
    iex> cred.presigned_url
    "https://bucket.s3.us-east-1.amazonaws.com/file.jpg?X-Amz-..."
    iex> cred.uptoken
    nil

---

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