Amazon S3 and services that implement its API, such as MinIO, Adobe S3Mock, Cloudflare R2, Backblaze B2, Tigris and Ceph.
disk =
Fil.disk(
adapter: Fil.Adapter.S3,
bucket: "invoices",
region: "eu-central-1",
access_key_id: System.fetch_env!("AWS_ACCESS_KEY_ID"),
secret_access_key: System.fetch_env!("AWS_SECRET_ACCESS_KEY")
)Requests are sent and signed (SigV4) by Req, configured with :req_options. Listings are
parsed with OTP's :xmerl_sax_parser.
Checksums
S3 stores a checksum with an object when the write sends one. With checksum: :sha256 (or :sha1, :crc32),
Fil.write/4 sends the checksum of the content, S3 rejects the upload if what it received doesn't match, and
Fil.stat/3 with the same :checksum option returns the stored value. Fil.read/3 with verify_checksum: true asks
S3 for the stored checksum and compares it with the downloaded content. Objects stored with another algorithm, or with
none, are read without a check.
Options
:bucket(String.t/0) - Required. The bucket all paths are stored in.:region(String.t/0) - The bucket's region. The default value is"us-east-1".:root(String.t/0) - The key prefix every path is resolved against, e.g."uploads". Paths on the disk stay relative to it, and listings strip it again. Defaults to the bucket root. The default value is".".:access_key_id(String.t/0) - The access key.Fildoesn't look up credentials itself (no environment variables, no instance metadata), so where they come from is up to the application. Leave out all credentials to access a public bucket without signing.:secret_access_key(String.t/0) - The secret matching:access_key_id.:session_token(String.t/0) - The session token, for temporary STS credentials.:endpoint(String.t/0) - A base URL for S3-compatible services, e.g."http://localhost:8333". Setting it turns:path_styleon.:path_style(boolean/0) - Put the bucket in the path (https://host/bucket/key) instead of the hostname (https://bucket.host/key). Defaults totruewhen:endpointis set,falseotherwise.:req_options(keyword/0) - Options for every Req request the disk makes, such as:receive_timeout,:connect_optionsor a shared:finchpool. The adapter always sets:method,:url,:headersand:body, plusretry: false(retrying is up to the caller) andraw: true(no decompression and no body decoding, so a file reads back exactly as it was written). The default value is[].
Operations
Fil | S3 |
|---|---|
read/3 | GetObject (x-amz-checksum-mode: ENABLED for verify_checksum: true) |
write/4 | PutObject (If-None-Match: * for if_exists: :error, x-amz-checksum-* for checksum:) |
rm/3 | DeleteObject, which S3 already treats as idempotent (a 404 for a missing bucket is still an error) |
stat/3 | HeadObject (x-amz-checksum-mode: ENABLED for checksum:), then a prefix probe so dir?/1 works |
ls/3 | ListObjectsV2, delimiter=/ unless recursive, paginated internally |
cp/4 | CopyObject |
rename/4 | CopyObject, then DeleteObject |
rm_rf/3 | ListObjectsV2, then one DeleteObject per key |
url/2 | the object URL, without a signature (works for public objects only) |
signed_url/3 | a presigned GET or PUT URL |
Errors
:reason is the error code from the response body ("NoSuchKey"), or {:http_status, status} when there's none.
Failures before a response keep Req's reason (:timeout, an exception). The error code decides first, whatever the
status, because S3-compatible servers don't all send the same one.
| S3 response | Fil error |
|---|---|
NoSuchKey, or 404 | Fil.NotFoundError |
NoSuchBucket | Fil.ConfigurationError |
a 400 for a copy whose source doesn't exist (checked with HeadObject) | Fil.NotFoundError |
AccessDenied, or 403 | Fil.AccessDeniedError |
EntityTooLarge, KeyTooLongError | Fil.InvalidRequestError |
PreconditionFailed, ConditionalRequestConflict | Fil.AlreadyExistsError |
412, or a 409 without a code | Fil.AlreadyExistsError |
BadDigest | Fil.ChecksumMismatchError |
| a body that doesn't match its stored checksum | Fil.ChecksumMismatchError, reason: :checksum_mismatch |
| a signed URL on a disk without credentials | Fil.UnsupportedError, reason: :missing_credentials |
301, or a 400 that gives another region | Fil.ConfigurationError, reason: {:wrong_region, region} |
SlowDown, OperationAborted, InternalError, ServiceUnavailable | Fil.UnavailableError |
429, 5xx | Fil.UnavailableError |
| timeouts, failed connections, an unreadable listing | Fil.UnavailableError |
anything else, including an error inside the 200 of a CopyObject | Fil.UnknownError |