Skip to main content

Python (boto3)

Fil One works with boto3, the AWS SDK for Python — it is S3-API compatible, so no custom library is needed. Configure the client to point at the Fil One endpoint, enable path-style addressing, and use the standard API. A few S3 operations are not supported; see S3 Compatibility for the exceptions.

The endpoint depends on your bucket's region

Examples here use eu-west-1, the default region. For a bucket in us-east-1, use https://us-east-1.s3.filonecontent.com and region_name="us-east-1". The endpoint, the region, and your access key must all refer to the same region.

Installation

pip install boto3

Client configuration

import boto3
import os
from botocore.config import Config

s3 = boto3.client(
"s3",
endpoint_url="https://eu-west-1.s3.filonecontent.com",
aws_access_key_id=os.environ["FIL_ACCESS_KEY"],
aws_secret_access_key=os.environ["FIL_SECRET_KEY"],
region_name="eu-west-1",
config=Config(s3={"addressing_style": "path"}),
)

Set FIL_ACCESS_KEY and FIL_SECRET_KEY as environment variables. Never hardcode credentials in source code.

Path-style addressing is required

Fil One only supports path-style URLs (https://eu-west-1.s3.filonecontent.com/my-bucket/key). boto3 defaults to virtual-hosted style (https://my-bucket.eu-west-1.s3.filonecontent.com/key) even with a custom endpoint_url, which fails with a DNS or 404-shaped error. The config=Config(s3={"addressing_style": "path"}) line above is not optional — without it, every request fails. region_name is also required, since SigV4 cannot sign without a region.

Core operations

Create a bucket

CreateBucket is region-dependent. It is not available in eu-west-1 — use the Fil One dashboard there, via Buckets → Create bucket. In us-east-1, with a key that has the CreateBucket permission:

s3.create_bucket(Bucket="my-bucket")

Note that versioning and Object Lock can only be set when the bucket is created, and only from the dashboard. See Buckets.

Upload an object

# Upload from file path
s3.upload_file("report.pdf", "my-bucket", "reports/report.pdf")

# Upload with content type
s3.put_object(
Bucket="my-bucket",
Key="data/file.json",
Body=b'{"key": "value"}',
ContentType="application/json",
)

Download an object

s3.download_file("my-bucket", "reports/report.pdf", "local-report.pdf")

List objects

paginator = s3.get_paginator("list_objects_v2")
for page in paginator.paginate(Bucket="my-bucket"):
for obj in page.get("Contents", []):
print(obj["Key"], obj["Size"])

Delete an object

s3.delete_object(Bucket="my-bucket", Key="reports/report.pdf")

Multipart uploads

boto3's upload_file and upload_fileobj switch to multipart automatically for any file larger than the multipart_threshold, which defaults to 8 MB (not 5 GB). You can configure the threshold and concurrency:

from boto3.s3.transfer import TransferConfig

config = TransferConfig(
multipart_threshold=100 * 1024 * 1024, # 100 MB
multipart_chunksize=50 * 1024 * 1024, # 50 MB
max_concurrency=10,
)

s3.upload_file("large-file.zip", "my-bucket", "large-file.zip", Config=config)

Presigned URLs

# Download URL valid for 1 hour
download_url = s3.generate_presigned_url(
"get_object",
Params={"Bucket": "my-bucket", "Key": "reports/report.pdf"},
ExpiresIn=3600,
)

# Upload URL valid for 15 minutes
upload_url = s3.generate_presigned_url(
"put_object",
Params={"Bucket": "my-bucket", "Key": "uploads/new-file.txt"},
ExpiresIn=900,
)

Error handling

from botocore.exceptions import ClientError

try:
s3.get_object(Bucket="my-bucket", Key="missing-file.txt")
except ClientError as e:
code = e.response["Error"]["Code"]
if code == "NoSuchKey":
print("Object not found")
elif code == "AccessDenied":
print("Access denied — check key permissions")
else:
raise

See the Error Reference for a full list of error codes.