# s3 > Package s3. S3-compatible object storage, signed with AWS Signature Version 4: AWS S3, Cloudflare R2, MinIO and the rest. ```slang import "s3"; import "proc"; let r = s3.new_client("https://s3.eu-west-1.amazonaws.com", "eu-west-1", access_key, secret_key); // or s3.from_env(proc.getenv): AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, // AWS_SESSION_TOKEN, AWS_REGION, S3_ENDPOINT guard let c = r else let e = err_of(r) { panic(e); } s3.put_object(c, "uploads", "avatars/42.png", png, "image/png", deadline); // ok(etag) s3.get_object(c, "uploads", "avatars/42.png", deadline); // ok(bytes) s3.get_range(c, "uploads", "logs/today", 0, 1023, deadline); // first KiB s3.head_object(c, "uploads", "avatars/42.png", deadline); // ok(some(Object)) or ok(none) s3.delete_object(c, "uploads", "avatars/42.png", deadline); s3.list_objects(c, "uploads", "avatars/", "/", "", 1000, deadline); // ok(Page); page.next continues s3.put_large(c, "backups", "db.dump", dump, "", 16 * 1048576, deadline); // multipart s3.presign_get(c, "uploads", "avatars/42.png", 900); // ok(url) for 15 minutes s3.presign_put(c, "uploads", "incoming/x.bin", 300); ``` The payload is hashed into the signature (`x-amz-content-sha256`), so a body altered in transit is refused by the server. Signing keys are cached for the day they belong to. Errors carry S3's own code and message (`s3: NoSuchKey: The specified key does not exist.`). **Addressing and listing.** Addressing is path-style by default (`endpoint/bucket/key`), which MinIO, R2 and most compatible stores want. Set `c.path_style = false` for virtual-host style (`bucket.endpoint/key`). `list_objects` returns up to 1000 keys a page in key order. Pass `page.next` to get the next page; with a delimiter, deeper keys fold into `page.prefixes`. **Multipart.** `create_multipart`, `upload_part`, `complete_multipart` and `abort_multipart` are the raw steps (every part but the last at least 5 MiB). `put_large` drives them, uploading in one request when the data fits in a part, and aborting if a part fails so no orphaned parts are left to be billed. **Presigned URLs** last 1 second to 7 days. `presign_at` signs at a given time, for tests and for URLs several servers must agree on. **Limits.** Objects are read and written whole, in memory (or as one byte range). Redirects are not followed, since a redirect would change the host the signature covers. ## API ### `gc struct Client` ### `gc struct Object` ### `gc struct Page` ### `fn new_client(endpoint: str, region: str, access_key: str, secret_key: str) -> result[Client, str]` A client for endpoint ("https://s3.us-east-1.amazonaws.com", "https://.r2.cloudflarestorage.com", "http://127.0.0.1:9000"). ### `fn from_env(getenv: fn(str) -> opt[str]) -> result[Client, str]` A client from AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN (optional), AWS_REGION (or AWS_DEFAULT_REGION) and S3_ENDPOINT (default: the region's AWS endpoint). ### `fn put_object(c: Client, bucket: str, key: str, data: bytes, content_type: str, deadline: until) -> result[str, str]` Store data at bucket/key; the object's ETag. ### `fn get_object(c: Client, bucket: str, key: str, deadline: until) -> result[bytes, str]` ### `fn get_range(c: Client, bucket: str, key: str, first: int, last: int, deadline: until) -> result[bytes, str]` Bytes first..last of the object, both included (an HTTP Range). ### `fn head_object(c: Client, bucket: str, key: str, deadline: until) -> result[opt[Object], str]` Size, ETag, type and modification time, without the body. none when the object does not exist. ### `fn delete_object(c: Client, bucket: str, key: str, deadline: until) -> result[bool, str]` Delete bucket/key. S3 answers success for a key that was not there. ### `fn list_objects(c: Client, bucket: str, prefix: str, delimiter: str, next: str, max_keys: int, deadline: until) -> result[Page, str]` One page (up to max_keys, at most 1000) of the keys under prefix, in key order, from where `next` (a Page's next, or "" to start) says. With delimiter "/", keys below the next "/" fold into page.prefixes. ### `fn create_multipart(c: Client, bucket: str, key: str, content_type: str, deadline: until) -> result[str, str]` Start a multipart upload; its upload id. ### `fn upload_part(c: Client, bucket: str, key: str, upload_id: str, n: int, data: bytes, deadline: until) -> result[str, str]` Upload part number n (1 to 10000) of an upload; its ETag, for complete_multipart. Every part but the last must be at least 5 MiB. ### `fn complete_multipart(c: Client, bucket: str, key: str, upload_id: str, etags: [str], deadline: until) -> result[str, str]` Finish an upload from its parts' ETags, in part order; the object's ETag. ### `fn abort_multipart(c: Client, bucket: str, key: str, upload_id: str, deadline: until) -> result[bool, str]` ### `fn put_large(c: Client, bucket: str, key: str, data: bytes, content_type: str, part_size: int, deadline: until) -> result[str, str]` Upload data in parts of part_size bytes (at least 5 MiB), aborting the upload if any part fails, so no orphaned parts are left to be billed. ### `fn create_bucket(c: Client, bucket: str, deadline: until) -> result[bool, str]` ### `fn delete_bucket(c: Client, bucket: str, deadline: until) -> result[bool, str]` ### `fn presign_get(c: Client, bucket: str, key: str, expires_seconds: int) -> result[str, str]` A URL anyone can GET (or PUT, below) for expires_seconds (1 second to 7 days), signed now: for downloads and browser uploads that never see your keys. ### `fn presign_put(c: Client, bucket: str, key: str, expires_seconds: int) -> result[str, str]` ### `fn presign_at(c: Client, method: str, bucket: str, key: str, expires_seconds: int, amzdate: str) -> result[str, str]` presign at a given time ("20130524T000000Z"), for tests and for URLs that must agree across servers. ### `fn sign_request(c: Client, method: str, host: str, path: str, query: map[str]str, headers: map[str]str, payload_hash: str, amzdate: str) -> str` The Authorization header for a request (SigV4, signed headers are host, x-amz-content-sha256, x-amz-date, any x-amz-security-token and the caller's headers). headers holds lowercase names. ### `fn uri_encode(s: str, keep_slash: bool) -> str` RFC 3986 percent-encoding as SigV4 wants it: everything but A-Z a-z 0-9 - _ . ~ (and / in paths, when keep_slash). --- slang is built and maintained by **Dolphlabs** (Dolph Tech Limited) — https://dolphlabs.com