chub-dev 0.1.0 → 0.1.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- package/src/mcp/tools.js +251 -0
|
@@ -0,0 +1,1807 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: s3
|
|
3
|
+
description: "AWS S3 SDK for Python (boto3) - Complete guide for S3 operations in Python projects"
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "python"
|
|
6
|
+
versions: "1.40.59"
|
|
7
|
+
updated-on: "2026-03-02"
|
|
8
|
+
source: maintainer
|
|
9
|
+
tags: "aws,s3,storage,cloud,bucket"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# AWS S3 SDK for Python (boto3) - Complete Guide
|
|
13
|
+
|
|
14
|
+
## Golden Rule
|
|
15
|
+
|
|
16
|
+
**ALWAYS use `boto3` for AWS S3 operations in Python projects.**
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install boto3
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**DO NOT use:**
|
|
23
|
+
- `boto` (legacy library, deprecated)
|
|
24
|
+
- Any unofficial S3 libraries
|
|
25
|
+
|
|
26
|
+
`boto3` is the official AWS SDK for Python. It provides low-level client access and high-level object-oriented resource access to AWS services.
|
|
27
|
+
|
|
28
|
+
**Python Version Requirements:**
|
|
29
|
+
- Python 3.9 or later (support for Python 3.8 ended on 2025-04-22)
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
### Basic Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install boto3
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### With pip (specific version)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install boto3==1.40.59
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### With Poetry
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
poetry add boto3
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### With uv
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv add boto3
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Environment Variables
|
|
60
|
+
|
|
61
|
+
Create a `.env` file:
|
|
62
|
+
|
|
63
|
+
```env
|
|
64
|
+
AWS_ACCESS_KEY_ID=your_access_key_id
|
|
65
|
+
AWS_SECRET_ACCESS_KEY=your_secret_access_key
|
|
66
|
+
AWS_DEFAULT_REGION=us-east-1
|
|
67
|
+
AWS_S3_BUCKET=your-bucket-name
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Load environment variables in your code:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import os
|
|
74
|
+
from dotenv import load_dotenv
|
|
75
|
+
|
|
76
|
+
load_dotenv()
|
|
77
|
+
|
|
78
|
+
REGION = os.getenv("AWS_DEFAULT_REGION")
|
|
79
|
+
BUCKET = os.getenv("AWS_S3_BUCKET")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### AWS Credentials Configuration
|
|
83
|
+
|
|
84
|
+
Boto3 looks for credentials in this order:
|
|
85
|
+
|
|
86
|
+
1. Environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`)
|
|
87
|
+
2. Shared credentials file (`~/.aws/credentials`)
|
|
88
|
+
3. AWS config file (`~/.aws/config`)
|
|
89
|
+
4. IAM role (when running on EC2)
|
|
90
|
+
|
|
91
|
+
Configure credentials using AWS CLI:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
aws configure
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Initialization
|
|
100
|
+
|
|
101
|
+
### Client vs Resource
|
|
102
|
+
|
|
103
|
+
boto3 provides two interfaces:
|
|
104
|
+
|
|
105
|
+
- **Client**: Low-level service access (1-to-1 mapping with AWS APIs)
|
|
106
|
+
- **Resource**: Higher-level object-oriented interface
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import boto3
|
|
110
|
+
|
|
111
|
+
# Client (low-level)
|
|
112
|
+
s3_client = boto3.client("s3")
|
|
113
|
+
|
|
114
|
+
# Resource (high-level)
|
|
115
|
+
s3_resource = boto3.resource("s3")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Basic Client Setup
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
import boto3
|
|
122
|
+
|
|
123
|
+
# Default credentials from environment or AWS config
|
|
124
|
+
s3 = boto3.client("s3")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Client with Region
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
import boto3
|
|
131
|
+
|
|
132
|
+
s3 = boto3.client("s3", region_name="us-east-1")
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Client with Explicit Credentials
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
import boto3
|
|
139
|
+
|
|
140
|
+
s3 = boto3.client(
|
|
141
|
+
"s3",
|
|
142
|
+
aws_access_key_id="YOUR_ACCESS_KEY",
|
|
143
|
+
aws_secret_access_key="YOUR_SECRET_KEY",
|
|
144
|
+
region_name="us-east-1"
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Client with Session
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
import boto3
|
|
152
|
+
|
|
153
|
+
session = boto3.Session(
|
|
154
|
+
aws_access_key_id="YOUR_ACCESS_KEY",
|
|
155
|
+
aws_secret_access_key="YOUR_SECRET_KEY",
|
|
156
|
+
region_name="us-east-1"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
s3 = session.client("s3")
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Resource Setup
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
import boto3
|
|
166
|
+
|
|
167
|
+
s3 = boto3.resource("s3")
|
|
168
|
+
|
|
169
|
+
# With region
|
|
170
|
+
s3 = boto3.resource("s3", region_name="us-west-2")
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Client with Custom Endpoint (LocalStack, MinIO)
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
import boto3
|
|
177
|
+
|
|
178
|
+
s3 = boto3.client(
|
|
179
|
+
"s3",
|
|
180
|
+
endpoint_url="http://localhost:4566",
|
|
181
|
+
aws_access_key_id="test",
|
|
182
|
+
aws_secret_access_key="test",
|
|
183
|
+
region_name="us-east-1"
|
|
184
|
+
)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Bucket Operations
|
|
190
|
+
|
|
191
|
+
### List All Buckets
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
import boto3
|
|
195
|
+
|
|
196
|
+
s3 = boto3.client("s3")
|
|
197
|
+
|
|
198
|
+
def list_buckets():
|
|
199
|
+
try:
|
|
200
|
+
response = s3.list_buckets()
|
|
201
|
+
|
|
202
|
+
print("Buckets:")
|
|
203
|
+
for bucket in response["Buckets"]:
|
|
204
|
+
print(f" • {bucket['Name']} (Created: {bucket['CreationDate']})")
|
|
205
|
+
except Exception as e:
|
|
206
|
+
print(f"Error listing buckets: {e}")
|
|
207
|
+
|
|
208
|
+
# Using resource
|
|
209
|
+
s3 = boto3.resource("s3")
|
|
210
|
+
|
|
211
|
+
def list_buckets_resource():
|
|
212
|
+
for bucket in s3.buckets.all():
|
|
213
|
+
print(f" • {bucket.name}")
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Create Bucket
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
import boto3
|
|
220
|
+
|
|
221
|
+
s3 = boto3.client("s3")
|
|
222
|
+
|
|
223
|
+
def create_bucket(bucket_name):
|
|
224
|
+
try:
|
|
225
|
+
s3.create_bucket(Bucket=bucket_name)
|
|
226
|
+
print(f"Bucket created: {bucket_name}")
|
|
227
|
+
except Exception as e:
|
|
228
|
+
print(f"Error creating bucket: {e}")
|
|
229
|
+
|
|
230
|
+
# Using resource
|
|
231
|
+
s3 = boto3.resource("s3")
|
|
232
|
+
|
|
233
|
+
def create_bucket_resource(bucket_name):
|
|
234
|
+
try:
|
|
235
|
+
s3.create_bucket(Bucket=bucket_name)
|
|
236
|
+
print(f"Bucket created: {bucket_name}")
|
|
237
|
+
except Exception as e:
|
|
238
|
+
print(f"Error: {e}")
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Create Bucket in Specific Region
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
import boto3
|
|
245
|
+
|
|
246
|
+
s3 = boto3.client("s3", region_name="us-west-2")
|
|
247
|
+
|
|
248
|
+
def create_bucket_in_region(bucket_name, region):
|
|
249
|
+
try:
|
|
250
|
+
if region == "us-east-1":
|
|
251
|
+
s3.create_bucket(Bucket=bucket_name)
|
|
252
|
+
else:
|
|
253
|
+
s3.create_bucket(
|
|
254
|
+
Bucket=bucket_name,
|
|
255
|
+
CreateBucketConfiguration={"LocationConstraint": region}
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
print(f"Bucket created in {region}")
|
|
259
|
+
except Exception as e:
|
|
260
|
+
print(f"Error creating bucket: {e}")
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Delete Bucket
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
import boto3
|
|
267
|
+
|
|
268
|
+
s3 = boto3.client("s3")
|
|
269
|
+
|
|
270
|
+
def delete_bucket(bucket_name):
|
|
271
|
+
try:
|
|
272
|
+
s3.delete_bucket(Bucket=bucket_name)
|
|
273
|
+
print(f"Bucket deleted: {bucket_name}")
|
|
274
|
+
except Exception as e:
|
|
275
|
+
print(f"Error deleting bucket: {e}")
|
|
276
|
+
|
|
277
|
+
# Using resource
|
|
278
|
+
s3 = boto3.resource("s3")
|
|
279
|
+
|
|
280
|
+
def delete_bucket_resource(bucket_name):
|
|
281
|
+
try:
|
|
282
|
+
bucket = s3.Bucket(bucket_name)
|
|
283
|
+
bucket.delete()
|
|
284
|
+
print(f"Bucket deleted: {bucket_name}")
|
|
285
|
+
except Exception as e:
|
|
286
|
+
print(f"Error: {e}")
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Check if Bucket Exists
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
import boto3
|
|
293
|
+
from botocore.exceptions import ClientError
|
|
294
|
+
|
|
295
|
+
s3 = boto3.client("s3")
|
|
296
|
+
|
|
297
|
+
def bucket_exists(bucket_name):
|
|
298
|
+
try:
|
|
299
|
+
s3.head_bucket(Bucket=bucket_name)
|
|
300
|
+
return True
|
|
301
|
+
except ClientError as e:
|
|
302
|
+
if e.response["Error"]["Code"] == "404":
|
|
303
|
+
return False
|
|
304
|
+
raise
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Get Bucket Location
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
import boto3
|
|
311
|
+
|
|
312
|
+
s3 = boto3.client("s3")
|
|
313
|
+
|
|
314
|
+
def get_bucket_location(bucket_name):
|
|
315
|
+
try:
|
|
316
|
+
response = s3.get_bucket_location(Bucket=bucket_name)
|
|
317
|
+
location = response["LocationConstraint"]
|
|
318
|
+
|
|
319
|
+
# us-east-1 returns None
|
|
320
|
+
if location is None:
|
|
321
|
+
location = "us-east-1"
|
|
322
|
+
|
|
323
|
+
print(f"Bucket location: {location}")
|
|
324
|
+
return location
|
|
325
|
+
except Exception as e:
|
|
326
|
+
print(f"Error: {e}")
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Object Upload Operations
|
|
332
|
+
|
|
333
|
+
### Upload File from Disk
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
import boto3
|
|
337
|
+
|
|
338
|
+
s3 = boto3.client("s3")
|
|
339
|
+
|
|
340
|
+
def upload_file(file_path, bucket_name, object_key):
|
|
341
|
+
try:
|
|
342
|
+
s3.upload_file(file_path, bucket_name, object_key)
|
|
343
|
+
print(f"Uploaded {file_path} to {bucket_name}/{object_key}")
|
|
344
|
+
except Exception as e:
|
|
345
|
+
print(f"Upload error: {e}")
|
|
346
|
+
|
|
347
|
+
# Using resource
|
|
348
|
+
s3 = boto3.resource("s3")
|
|
349
|
+
|
|
350
|
+
def upload_file_resource(file_path, bucket_name, object_key):
|
|
351
|
+
try:
|
|
352
|
+
bucket = s3.Bucket(bucket_name)
|
|
353
|
+
bucket.upload_file(file_path, object_key)
|
|
354
|
+
print(f"Uploaded {file_path}")
|
|
355
|
+
except Exception as e:
|
|
356
|
+
print(f"Upload error: {e}")
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Upload with put_object
|
|
360
|
+
|
|
361
|
+
```python
|
|
362
|
+
import boto3
|
|
363
|
+
|
|
364
|
+
s3 = boto3.client("s3")
|
|
365
|
+
|
|
366
|
+
def upload_object(bucket_name, key, data):
|
|
367
|
+
try:
|
|
368
|
+
s3.put_object(
|
|
369
|
+
Bucket=bucket_name,
|
|
370
|
+
Key=key,
|
|
371
|
+
Body=data
|
|
372
|
+
)
|
|
373
|
+
print(f"Uploaded to {key}")
|
|
374
|
+
except Exception as e:
|
|
375
|
+
print(f"Upload error: {e}")
|
|
376
|
+
|
|
377
|
+
# Upload string
|
|
378
|
+
upload_object("my-bucket", "file.txt", b"Hello, S3!")
|
|
379
|
+
|
|
380
|
+
# Upload JSON
|
|
381
|
+
import json
|
|
382
|
+
upload_object("my-bucket", "data.json", json.dumps({"key": "value"}))
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Upload with Content Type
|
|
386
|
+
|
|
387
|
+
```python
|
|
388
|
+
import boto3
|
|
389
|
+
|
|
390
|
+
s3 = boto3.client("s3")
|
|
391
|
+
|
|
392
|
+
def upload_with_content_type(bucket_name, key, file_path, content_type):
|
|
393
|
+
try:
|
|
394
|
+
s3.upload_file(
|
|
395
|
+
file_path,
|
|
396
|
+
bucket_name,
|
|
397
|
+
key,
|
|
398
|
+
ExtraArgs={"ContentType": content_type}
|
|
399
|
+
)
|
|
400
|
+
print(f"Uploaded {key} as {content_type}")
|
|
401
|
+
except Exception as e:
|
|
402
|
+
print(f"Upload error: {e}")
|
|
403
|
+
|
|
404
|
+
# Usage
|
|
405
|
+
upload_with_content_type(
|
|
406
|
+
"my-bucket",
|
|
407
|
+
"image.jpg",
|
|
408
|
+
"./photo.jpg",
|
|
409
|
+
"image/jpeg"
|
|
410
|
+
)
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Upload with Metadata
|
|
414
|
+
|
|
415
|
+
```python
|
|
416
|
+
import boto3
|
|
417
|
+
|
|
418
|
+
s3 = boto3.client("s3")
|
|
419
|
+
|
|
420
|
+
def upload_with_metadata(bucket_name, key, file_path):
|
|
421
|
+
try:
|
|
422
|
+
s3.upload_file(
|
|
423
|
+
file_path,
|
|
424
|
+
bucket_name,
|
|
425
|
+
key,
|
|
426
|
+
ExtraArgs={
|
|
427
|
+
"Metadata": {
|
|
428
|
+
"uploaded-by": "user123",
|
|
429
|
+
"original-name": "document.pdf",
|
|
430
|
+
"category": "reports"
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
)
|
|
434
|
+
print("Uploaded with metadata")
|
|
435
|
+
except Exception as e:
|
|
436
|
+
print(f"Upload error: {e}")
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Upload with Server-Side Encryption
|
|
440
|
+
|
|
441
|
+
```python
|
|
442
|
+
import boto3
|
|
443
|
+
|
|
444
|
+
s3 = boto3.client("s3")
|
|
445
|
+
|
|
446
|
+
def upload_encrypted(bucket_name, key, file_path):
|
|
447
|
+
try:
|
|
448
|
+
s3.upload_file(
|
|
449
|
+
file_path,
|
|
450
|
+
bucket_name,
|
|
451
|
+
key,
|
|
452
|
+
ExtraArgs={"ServerSideEncryption": "AES256"}
|
|
453
|
+
)
|
|
454
|
+
print("Uploaded with encryption")
|
|
455
|
+
except Exception as e:
|
|
456
|
+
print(f"Upload error: {e}")
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Upload with ACL
|
|
460
|
+
|
|
461
|
+
```python
|
|
462
|
+
import boto3
|
|
463
|
+
|
|
464
|
+
s3 = boto3.client("s3")
|
|
465
|
+
|
|
466
|
+
def upload_with_acl(bucket_name, key, file_path):
|
|
467
|
+
try:
|
|
468
|
+
s3.upload_file(
|
|
469
|
+
file_path,
|
|
470
|
+
bucket_name,
|
|
471
|
+
key,
|
|
472
|
+
ExtraArgs={"ACL": "public-read"}
|
|
473
|
+
)
|
|
474
|
+
print("Uploaded as public")
|
|
475
|
+
except Exception as e:
|
|
476
|
+
print(f"Upload error: {e}")
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Upload Binary Data
|
|
480
|
+
|
|
481
|
+
```python
|
|
482
|
+
import boto3
|
|
483
|
+
|
|
484
|
+
s3 = boto3.client("s3")
|
|
485
|
+
|
|
486
|
+
def upload_binary(bucket_name, key, data):
|
|
487
|
+
try:
|
|
488
|
+
s3.put_object(
|
|
489
|
+
Bucket=bucket_name,
|
|
490
|
+
Key=key,
|
|
491
|
+
Body=data,
|
|
492
|
+
ContentType="application/octet-stream"
|
|
493
|
+
)
|
|
494
|
+
print(f"Uploaded binary data to {key}")
|
|
495
|
+
except Exception as e:
|
|
496
|
+
print(f"Upload error: {e}")
|
|
497
|
+
|
|
498
|
+
# Usage
|
|
499
|
+
with open("image.png", "rb") as f:
|
|
500
|
+
upload_binary("my-bucket", "uploads/image.png", f.read())
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Batch Upload Multiple Files
|
|
504
|
+
|
|
505
|
+
```python
|
|
506
|
+
import boto3
|
|
507
|
+
import os
|
|
508
|
+
|
|
509
|
+
s3 = boto3.client("s3")
|
|
510
|
+
|
|
511
|
+
def upload_directory(directory_path, bucket_name, s3_prefix=""):
|
|
512
|
+
try:
|
|
513
|
+
for root, dirs, files in os.walk(directory_path):
|
|
514
|
+
for file in files:
|
|
515
|
+
local_path = os.path.join(root, file)
|
|
516
|
+
relative_path = os.path.relpath(local_path, directory_path)
|
|
517
|
+
s3_key = os.path.join(s3_prefix, relative_path).replace("\\", "/")
|
|
518
|
+
|
|
519
|
+
s3.upload_file(local_path, bucket_name, s3_key)
|
|
520
|
+
print(f"Uploaded: {s3_key}")
|
|
521
|
+
|
|
522
|
+
print(f"Uploaded directory: {directory_path}")
|
|
523
|
+
except Exception as e:
|
|
524
|
+
print(f"Batch upload error: {e}")
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## Object Download Operations
|
|
530
|
+
|
|
531
|
+
### Download File to Disk
|
|
532
|
+
|
|
533
|
+
```python
|
|
534
|
+
import boto3
|
|
535
|
+
|
|
536
|
+
s3 = boto3.client("s3")
|
|
537
|
+
|
|
538
|
+
def download_file(bucket_name, key, file_path):
|
|
539
|
+
try:
|
|
540
|
+
s3.download_file(bucket_name, key, file_path)
|
|
541
|
+
print(f"Downloaded to {file_path}")
|
|
542
|
+
except Exception as e:
|
|
543
|
+
print(f"Download error: {e}")
|
|
544
|
+
|
|
545
|
+
# Using resource
|
|
546
|
+
s3 = boto3.resource("s3")
|
|
547
|
+
|
|
548
|
+
def download_file_resource(bucket_name, key, file_path):
|
|
549
|
+
try:
|
|
550
|
+
bucket = s3.Bucket(bucket_name)
|
|
551
|
+
bucket.download_file(key, file_path)
|
|
552
|
+
print(f"Downloaded to {file_path}")
|
|
553
|
+
except Exception as e:
|
|
554
|
+
print(f"Download error: {e}")
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Download as Bytes
|
|
558
|
+
|
|
559
|
+
```python
|
|
560
|
+
import boto3
|
|
561
|
+
|
|
562
|
+
s3 = boto3.client("s3")
|
|
563
|
+
|
|
564
|
+
def download_as_bytes(bucket_name, key):
|
|
565
|
+
try:
|
|
566
|
+
response = s3.get_object(Bucket=bucket_name, Key=key)
|
|
567
|
+
data = response["Body"].read()
|
|
568
|
+
return data
|
|
569
|
+
except Exception as e:
|
|
570
|
+
print(f"Download error: {e}")
|
|
571
|
+
return None
|
|
572
|
+
|
|
573
|
+
# Usage
|
|
574
|
+
data = download_as_bytes("my-bucket", "file.txt")
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### Download as String
|
|
578
|
+
|
|
579
|
+
```python
|
|
580
|
+
import boto3
|
|
581
|
+
|
|
582
|
+
s3 = boto3.client("s3")
|
|
583
|
+
|
|
584
|
+
def download_as_string(bucket_name, key, encoding="utf-8"):
|
|
585
|
+
try:
|
|
586
|
+
response = s3.get_object(Bucket=bucket_name, Key=key)
|
|
587
|
+
content = response["Body"].read().decode(encoding)
|
|
588
|
+
return content
|
|
589
|
+
except Exception as e:
|
|
590
|
+
print(f"Download error: {e}")
|
|
591
|
+
return None
|
|
592
|
+
|
|
593
|
+
# Download JSON
|
|
594
|
+
import json
|
|
595
|
+
|
|
596
|
+
def download_json(bucket_name, key):
|
|
597
|
+
content = download_as_string(bucket_name, key)
|
|
598
|
+
if content:
|
|
599
|
+
return json.loads(content)
|
|
600
|
+
return None
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### Download with Error Handling
|
|
604
|
+
|
|
605
|
+
```python
|
|
606
|
+
import boto3
|
|
607
|
+
from botocore.exceptions import ClientError
|
|
608
|
+
|
|
609
|
+
s3 = boto3.client("s3")
|
|
610
|
+
|
|
611
|
+
def download_safe(bucket_name, key, file_path):
|
|
612
|
+
try:
|
|
613
|
+
s3.download_file(bucket_name, key, file_path)
|
|
614
|
+
print(f"Downloaded to {file_path}")
|
|
615
|
+
return True
|
|
616
|
+
except ClientError as e:
|
|
617
|
+
if e.response["Error"]["Code"] == "404":
|
|
618
|
+
print(f"Object not found: {key}")
|
|
619
|
+
else:
|
|
620
|
+
print(f"Error: {e}")
|
|
621
|
+
return False
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
### Download with Version
|
|
625
|
+
|
|
626
|
+
```python
|
|
627
|
+
import boto3
|
|
628
|
+
|
|
629
|
+
s3 = boto3.client("s3")
|
|
630
|
+
|
|
631
|
+
def download_version(bucket_name, key, file_path, version_id):
|
|
632
|
+
try:
|
|
633
|
+
s3.download_file(
|
|
634
|
+
bucket_name,
|
|
635
|
+
key,
|
|
636
|
+
file_path,
|
|
637
|
+
ExtraArgs={"VersionId": version_id}
|
|
638
|
+
)
|
|
639
|
+
print(f"Downloaded version {version_id}")
|
|
640
|
+
except Exception as e:
|
|
641
|
+
print(f"Download error: {e}")
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Download Byte Range
|
|
645
|
+
|
|
646
|
+
```python
|
|
647
|
+
import boto3
|
|
648
|
+
|
|
649
|
+
s3 = boto3.client("s3")
|
|
650
|
+
|
|
651
|
+
def download_range(bucket_name, key, start, end):
|
|
652
|
+
try:
|
|
653
|
+
response = s3.get_object(
|
|
654
|
+
Bucket=bucket_name,
|
|
655
|
+
Key=key,
|
|
656
|
+
Range=f"bytes={start}-{end}"
|
|
657
|
+
)
|
|
658
|
+
data = response["Body"].read()
|
|
659
|
+
return data
|
|
660
|
+
except Exception as e:
|
|
661
|
+
print(f"Download error: {e}")
|
|
662
|
+
return None
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### Get Object Metadata Only
|
|
666
|
+
|
|
667
|
+
```python
|
|
668
|
+
import boto3
|
|
669
|
+
|
|
670
|
+
s3 = boto3.client("s3")
|
|
671
|
+
|
|
672
|
+
def get_object_metadata(bucket_name, key):
|
|
673
|
+
try:
|
|
674
|
+
response = s3.head_object(Bucket=bucket_name, Key=key)
|
|
675
|
+
|
|
676
|
+
print("Metadata:")
|
|
677
|
+
print(f" Content-Type: {response.get('ContentType')}")
|
|
678
|
+
print(f" Content-Length: {response.get('ContentLength')} bytes")
|
|
679
|
+
print(f" Last Modified: {response.get('LastModified')}")
|
|
680
|
+
print(f" ETag: {response.get('ETag')}")
|
|
681
|
+
print(f" Metadata: {response.get('Metadata')}")
|
|
682
|
+
|
|
683
|
+
return response
|
|
684
|
+
except Exception as e:
|
|
685
|
+
print(f"Error: {e}")
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
## List Objects
|
|
691
|
+
|
|
692
|
+
### List All Objects
|
|
693
|
+
|
|
694
|
+
```python
|
|
695
|
+
import boto3
|
|
696
|
+
|
|
697
|
+
s3 = boto3.client("s3")
|
|
698
|
+
|
|
699
|
+
def list_objects(bucket_name):
|
|
700
|
+
try:
|
|
701
|
+
response = s3.list_objects_v2(Bucket=bucket_name)
|
|
702
|
+
|
|
703
|
+
if "Contents" in response:
|
|
704
|
+
print("Objects:")
|
|
705
|
+
for obj in response["Contents"]:
|
|
706
|
+
print(f" • {obj['Key']} ({obj['Size']} bytes)")
|
|
707
|
+
else:
|
|
708
|
+
print("Bucket is empty")
|
|
709
|
+
except Exception as e:
|
|
710
|
+
print(f"List error: {e}")
|
|
711
|
+
|
|
712
|
+
# Using resource
|
|
713
|
+
s3 = boto3.resource("s3")
|
|
714
|
+
|
|
715
|
+
def list_objects_resource(bucket_name):
|
|
716
|
+
try:
|
|
717
|
+
bucket = s3.Bucket(bucket_name)
|
|
718
|
+
|
|
719
|
+
for obj in bucket.objects.all():
|
|
720
|
+
print(f" • {obj.key} ({obj.size} bytes)")
|
|
721
|
+
except Exception as e:
|
|
722
|
+
print(f"List error: {e}")
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
### List Objects with Prefix
|
|
726
|
+
|
|
727
|
+
```python
|
|
728
|
+
import boto3
|
|
729
|
+
|
|
730
|
+
s3 = boto3.client("s3")
|
|
731
|
+
|
|
732
|
+
def list_objects_with_prefix(bucket_name, prefix):
|
|
733
|
+
try:
|
|
734
|
+
response = s3.list_objects_v2(Bucket=bucket_name, Prefix=prefix)
|
|
735
|
+
|
|
736
|
+
if "Contents" in response:
|
|
737
|
+
print(f"Objects with prefix '{prefix}':")
|
|
738
|
+
for obj in response["Contents"]:
|
|
739
|
+
print(f" • {obj['Key']}")
|
|
740
|
+
else:
|
|
741
|
+
print(f"No objects with prefix '{prefix}'")
|
|
742
|
+
except Exception as e:
|
|
743
|
+
print(f"List error: {e}")
|
|
744
|
+
|
|
745
|
+
# Using resource
|
|
746
|
+
s3 = boto3.resource("s3")
|
|
747
|
+
|
|
748
|
+
def list_objects_with_prefix_resource(bucket_name, prefix):
|
|
749
|
+
bucket = s3.Bucket(bucket_name)
|
|
750
|
+
|
|
751
|
+
for obj in bucket.objects.filter(Prefix=prefix):
|
|
752
|
+
print(f" • {obj.key}")
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
### List Objects with Pagination
|
|
756
|
+
|
|
757
|
+
```python
|
|
758
|
+
import boto3
|
|
759
|
+
|
|
760
|
+
s3 = boto3.client("s3")
|
|
761
|
+
|
|
762
|
+
def list_objects_paginated(bucket_name):
|
|
763
|
+
try:
|
|
764
|
+
paginator = s3.get_paginator("list_objects_v2")
|
|
765
|
+
page_iterator = paginator.paginate(Bucket=bucket_name)
|
|
766
|
+
|
|
767
|
+
total_objects = 0
|
|
768
|
+
|
|
769
|
+
for page in page_iterator:
|
|
770
|
+
if "Contents" in page:
|
|
771
|
+
for obj in page["Contents"]:
|
|
772
|
+
print(f" • {obj['Key']}")
|
|
773
|
+
total_objects += 1
|
|
774
|
+
|
|
775
|
+
print(f"\nTotal objects: {total_objects}")
|
|
776
|
+
except Exception as e:
|
|
777
|
+
print(f"List error: {e}")
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
### List Objects with Max Keys
|
|
781
|
+
|
|
782
|
+
```python
|
|
783
|
+
import boto3
|
|
784
|
+
|
|
785
|
+
s3 = boto3.client("s3")
|
|
786
|
+
|
|
787
|
+
def list_objects_limited(bucket_name, max_keys=10):
|
|
788
|
+
try:
|
|
789
|
+
response = s3.list_objects_v2(Bucket=bucket_name, MaxKeys=max_keys)
|
|
790
|
+
|
|
791
|
+
if "Contents" in response:
|
|
792
|
+
for obj in response["Contents"]:
|
|
793
|
+
print(f" • {obj['Key']}")
|
|
794
|
+
|
|
795
|
+
print(f"\nShowing {len(response['Contents'])} objects")
|
|
796
|
+
print(f"More available: {response.get('IsTruncated', False)}")
|
|
797
|
+
except Exception as e:
|
|
798
|
+
print(f"List error: {e}")
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
### List Objects in Folder Structure
|
|
802
|
+
|
|
803
|
+
```python
|
|
804
|
+
import boto3
|
|
805
|
+
|
|
806
|
+
s3 = boto3.client("s3")
|
|
807
|
+
|
|
808
|
+
def list_folder(bucket_name, prefix=""):
|
|
809
|
+
try:
|
|
810
|
+
response = s3.list_objects_v2(
|
|
811
|
+
Bucket=bucket_name,
|
|
812
|
+
Prefix=prefix,
|
|
813
|
+
Delimiter="/"
|
|
814
|
+
)
|
|
815
|
+
|
|
816
|
+
# List folders (common prefixes)
|
|
817
|
+
if "CommonPrefixes" in response:
|
|
818
|
+
print("Folders:")
|
|
819
|
+
for cp in response["CommonPrefixes"]:
|
|
820
|
+
print(f" 📁 {cp['Prefix']}")
|
|
821
|
+
|
|
822
|
+
# List files
|
|
823
|
+
if "Contents" in response:
|
|
824
|
+
print("\nFiles:")
|
|
825
|
+
for obj in response["Contents"]:
|
|
826
|
+
print(f" 📄 {obj['Key']}")
|
|
827
|
+
except Exception as e:
|
|
828
|
+
print(f"List error: {e}")
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
### List with Filter
|
|
832
|
+
|
|
833
|
+
```python
|
|
834
|
+
import boto3
|
|
835
|
+
|
|
836
|
+
s3 = boto3.resource("s3")
|
|
837
|
+
|
|
838
|
+
def list_objects_filtered(bucket_name, extension):
|
|
839
|
+
try:
|
|
840
|
+
bucket = s3.Bucket(bucket_name)
|
|
841
|
+
|
|
842
|
+
for obj in bucket.objects.all():
|
|
843
|
+
if obj.key.endswith(extension):
|
|
844
|
+
print(f" • {obj.key}")
|
|
845
|
+
except Exception as e:
|
|
846
|
+
print(f"List error: {e}")
|
|
847
|
+
|
|
848
|
+
# Usage
|
|
849
|
+
list_objects_filtered("my-bucket", ".jpg")
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
---
|
|
853
|
+
|
|
854
|
+
## Delete Operations
|
|
855
|
+
|
|
856
|
+
### Delete Single Object
|
|
857
|
+
|
|
858
|
+
```python
|
|
859
|
+
import boto3
|
|
860
|
+
|
|
861
|
+
s3 = boto3.client("s3")
|
|
862
|
+
|
|
863
|
+
def delete_object(bucket_name, key):
|
|
864
|
+
try:
|
|
865
|
+
s3.delete_object(Bucket=bucket_name, Key=key)
|
|
866
|
+
print(f"Deleted: {key}")
|
|
867
|
+
except Exception as e:
|
|
868
|
+
print(f"Delete error: {e}")
|
|
869
|
+
|
|
870
|
+
# Using resource
|
|
871
|
+
s3 = boto3.resource("s3")
|
|
872
|
+
|
|
873
|
+
def delete_object_resource(bucket_name, key):
|
|
874
|
+
try:
|
|
875
|
+
obj = s3.Object(bucket_name, key)
|
|
876
|
+
obj.delete()
|
|
877
|
+
print(f"Deleted: {key}")
|
|
878
|
+
except Exception as e:
|
|
879
|
+
print(f"Delete error: {e}")
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
### Delete Multiple Objects (Batch)
|
|
883
|
+
|
|
884
|
+
```python
|
|
885
|
+
import boto3
|
|
886
|
+
|
|
887
|
+
s3 = boto3.client("s3")
|
|
888
|
+
|
|
889
|
+
def delete_multiple_objects(bucket_name, keys):
|
|
890
|
+
try:
|
|
891
|
+
response = s3.delete_objects(
|
|
892
|
+
Bucket=bucket_name,
|
|
893
|
+
Delete={
|
|
894
|
+
"Objects": [{"Key": key} for key in keys]
|
|
895
|
+
}
|
|
896
|
+
)
|
|
897
|
+
|
|
898
|
+
if "Deleted" in response:
|
|
899
|
+
print(f"Deleted {len(response['Deleted'])} objects:")
|
|
900
|
+
for obj in response["Deleted"]:
|
|
901
|
+
print(f" • {obj['Key']}")
|
|
902
|
+
|
|
903
|
+
if "Errors" in response:
|
|
904
|
+
print("Errors:")
|
|
905
|
+
for error in response["Errors"]:
|
|
906
|
+
print(f" • {error['Key']}: {error['Message']}")
|
|
907
|
+
except Exception as e:
|
|
908
|
+
print(f"Batch delete error: {e}")
|
|
909
|
+
|
|
910
|
+
# Usage
|
|
911
|
+
delete_multiple_objects("my-bucket", ["file1.txt", "file2.txt", "file3.txt"])
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
### Delete All Objects with Prefix
|
|
915
|
+
|
|
916
|
+
```python
|
|
917
|
+
import boto3
|
|
918
|
+
|
|
919
|
+
s3 = boto3.client("s3")
|
|
920
|
+
|
|
921
|
+
def delete_objects_with_prefix(bucket_name, prefix):
|
|
922
|
+
try:
|
|
923
|
+
response = s3.list_objects_v2(Bucket=bucket_name, Prefix=prefix)
|
|
924
|
+
|
|
925
|
+
if "Contents" not in response:
|
|
926
|
+
print("No objects to delete")
|
|
927
|
+
return
|
|
928
|
+
|
|
929
|
+
keys = [obj["Key"] for obj in response["Contents"]]
|
|
930
|
+
|
|
931
|
+
s3.delete_objects(
|
|
932
|
+
Bucket=bucket_name,
|
|
933
|
+
Delete={"Objects": [{"Key": key} for key in keys]}
|
|
934
|
+
)
|
|
935
|
+
|
|
936
|
+
print(f"Deleted {len(keys)} objects with prefix '{prefix}'")
|
|
937
|
+
except Exception as e:
|
|
938
|
+
print(f"Delete error: {e}")
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
### Delete All Objects in Bucket
|
|
942
|
+
|
|
943
|
+
```python
|
|
944
|
+
import boto3
|
|
945
|
+
|
|
946
|
+
s3 = boto3.resource("s3")
|
|
947
|
+
|
|
948
|
+
def empty_bucket(bucket_name):
|
|
949
|
+
try:
|
|
950
|
+
bucket = s3.Bucket(bucket_name)
|
|
951
|
+
bucket.objects.all().delete()
|
|
952
|
+
print(f"Emptied bucket: {bucket_name}")
|
|
953
|
+
except Exception as e:
|
|
954
|
+
print(f"Empty error: {e}")
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
### Empty and Delete Bucket
|
|
958
|
+
|
|
959
|
+
```python
|
|
960
|
+
import boto3
|
|
961
|
+
|
|
962
|
+
s3 = boto3.resource("s3")
|
|
963
|
+
|
|
964
|
+
def empty_and_delete_bucket(bucket_name):
|
|
965
|
+
try:
|
|
966
|
+
bucket = s3.Bucket(bucket_name)
|
|
967
|
+
|
|
968
|
+
# Delete all objects
|
|
969
|
+
bucket.objects.all().delete()
|
|
970
|
+
print(f"Deleted all objects")
|
|
971
|
+
|
|
972
|
+
# Delete all object versions (if versioning enabled)
|
|
973
|
+
bucket.object_versions.all().delete()
|
|
974
|
+
print(f"Deleted all object versions")
|
|
975
|
+
|
|
976
|
+
# Delete bucket
|
|
977
|
+
bucket.delete()
|
|
978
|
+
print(f"Deleted bucket: {bucket_name}")
|
|
979
|
+
except Exception as e:
|
|
980
|
+
print(f"Error: {e}")
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
---
|
|
984
|
+
|
|
985
|
+
## Copy and Move Operations
|
|
986
|
+
|
|
987
|
+
### Copy Object
|
|
988
|
+
|
|
989
|
+
```python
|
|
990
|
+
import boto3
|
|
991
|
+
|
|
992
|
+
s3 = boto3.client("s3")
|
|
993
|
+
|
|
994
|
+
def copy_object(source_bucket, source_key, dest_bucket, dest_key):
|
|
995
|
+
try:
|
|
996
|
+
copy_source = {"Bucket": source_bucket, "Key": source_key}
|
|
997
|
+
|
|
998
|
+
s3.copy_object(
|
|
999
|
+
CopySource=copy_source,
|
|
1000
|
+
Bucket=dest_bucket,
|
|
1001
|
+
Key=dest_key
|
|
1002
|
+
)
|
|
1003
|
+
|
|
1004
|
+
print(f"Copied {source_key} to {dest_key}")
|
|
1005
|
+
except Exception as e:
|
|
1006
|
+
print(f"Copy error: {e}")
|
|
1007
|
+
|
|
1008
|
+
# Using resource
|
|
1009
|
+
s3 = boto3.resource("s3")
|
|
1010
|
+
|
|
1011
|
+
def copy_object_resource(source_bucket, source_key, dest_bucket, dest_key):
|
|
1012
|
+
try:
|
|
1013
|
+
copy_source = {"Bucket": source_bucket, "Key": source_key}
|
|
1014
|
+
|
|
1015
|
+
dest_obj = s3.Object(dest_bucket, dest_key)
|
|
1016
|
+
dest_obj.copy_from(CopySource=copy_source)
|
|
1017
|
+
|
|
1018
|
+
print(f"Copied {source_key} to {dest_key}")
|
|
1019
|
+
except Exception as e:
|
|
1020
|
+
print(f"Copy error: {e}")
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
### Copy with Metadata
|
|
1024
|
+
|
|
1025
|
+
```python
|
|
1026
|
+
import boto3
|
|
1027
|
+
|
|
1028
|
+
s3 = boto3.client("s3")
|
|
1029
|
+
|
|
1030
|
+
def copy_with_metadata(source_bucket, source_key, dest_bucket, dest_key):
|
|
1031
|
+
try:
|
|
1032
|
+
copy_source = {"Bucket": source_bucket, "Key": source_key}
|
|
1033
|
+
|
|
1034
|
+
s3.copy_object(
|
|
1035
|
+
CopySource=copy_source,
|
|
1036
|
+
Bucket=dest_bucket,
|
|
1037
|
+
Key=dest_key,
|
|
1038
|
+
Metadata={
|
|
1039
|
+
"copied-from": source_key,
|
|
1040
|
+
"copied-date": "2025-10-26"
|
|
1041
|
+
},
|
|
1042
|
+
MetadataDirective="REPLACE"
|
|
1043
|
+
)
|
|
1044
|
+
|
|
1045
|
+
print("Copied with new metadata")
|
|
1046
|
+
except Exception as e:
|
|
1047
|
+
print(f"Copy error: {e}")
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
### Move Object (Copy + Delete)
|
|
1051
|
+
|
|
1052
|
+
```python
|
|
1053
|
+
import boto3
|
|
1054
|
+
|
|
1055
|
+
s3 = boto3.client("s3")
|
|
1056
|
+
|
|
1057
|
+
def move_object(source_bucket, source_key, dest_bucket, dest_key):
|
|
1058
|
+
try:
|
|
1059
|
+
# Copy
|
|
1060
|
+
copy_source = {"Bucket": source_bucket, "Key": source_key}
|
|
1061
|
+
s3.copy_object(
|
|
1062
|
+
CopySource=copy_source,
|
|
1063
|
+
Bucket=dest_bucket,
|
|
1064
|
+
Key=dest_key
|
|
1065
|
+
)
|
|
1066
|
+
|
|
1067
|
+
# Delete source
|
|
1068
|
+
s3.delete_object(Bucket=source_bucket, Key=source_key)
|
|
1069
|
+
|
|
1070
|
+
print(f"Moved {source_key} to {dest_key}")
|
|
1071
|
+
except Exception as e:
|
|
1072
|
+
print(f"Move error: {e}")
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
---
|
|
1076
|
+
|
|
1077
|
+
## Presigned URLs
|
|
1078
|
+
|
|
1079
|
+
### Presigned URL for Download (GET)
|
|
1080
|
+
|
|
1081
|
+
```python
|
|
1082
|
+
import boto3
|
|
1083
|
+
from botocore.config import Config
|
|
1084
|
+
|
|
1085
|
+
s3 = boto3.client("s3", config=Config(signature_version="s3v4"))
|
|
1086
|
+
|
|
1087
|
+
def create_presigned_download_url(bucket_name, key, expiration=3600):
|
|
1088
|
+
try:
|
|
1089
|
+
url = s3.generate_presigned_url(
|
|
1090
|
+
ClientMethod="get_object",
|
|
1091
|
+
Params={"Bucket": bucket_name, "Key": key},
|
|
1092
|
+
ExpiresIn=expiration
|
|
1093
|
+
)
|
|
1094
|
+
return url
|
|
1095
|
+
except Exception as e:
|
|
1096
|
+
print(f"Presigned URL error: {e}")
|
|
1097
|
+
return None
|
|
1098
|
+
|
|
1099
|
+
# Usage
|
|
1100
|
+
url = create_presigned_download_url("my-bucket", "file.pdf", 3600)
|
|
1101
|
+
print(f"Download URL: {url}")
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
### Presigned URL for Upload (PUT)
|
|
1105
|
+
|
|
1106
|
+
```python
|
|
1107
|
+
import boto3
|
|
1108
|
+
from botocore.config import Config
|
|
1109
|
+
|
|
1110
|
+
s3 = boto3.client("s3", config=Config(signature_version="s3v4"))
|
|
1111
|
+
|
|
1112
|
+
def create_presigned_upload_url(bucket_name, key, expiration=3600):
|
|
1113
|
+
try:
|
|
1114
|
+
url = s3.generate_presigned_url(
|
|
1115
|
+
ClientMethod="put_object",
|
|
1116
|
+
Params={"Bucket": bucket_name, "Key": key},
|
|
1117
|
+
ExpiresIn=expiration
|
|
1118
|
+
)
|
|
1119
|
+
return url
|
|
1120
|
+
except Exception as e:
|
|
1121
|
+
print(f"Presigned URL error: {e}")
|
|
1122
|
+
return None
|
|
1123
|
+
|
|
1124
|
+
# Usage with requests
|
|
1125
|
+
import requests
|
|
1126
|
+
|
|
1127
|
+
upload_url = create_presigned_upload_url("my-bucket", "upload.txt")
|
|
1128
|
+
|
|
1129
|
+
with open("file.txt", "rb") as f:
|
|
1130
|
+
response = requests.put(upload_url, data=f)
|
|
1131
|
+
print(f"Upload status: {response.status_code}")
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
### Presigned URL with Content Type
|
|
1135
|
+
|
|
1136
|
+
```python
|
|
1137
|
+
import boto3
|
|
1138
|
+
from botocore.config import Config
|
|
1139
|
+
|
|
1140
|
+
s3 = boto3.client("s3", config=Config(signature_version="s3v4"))
|
|
1141
|
+
|
|
1142
|
+
def create_presigned_url_with_type(bucket_name, key, content_type, expiration=3600):
|
|
1143
|
+
try:
|
|
1144
|
+
url = s3.generate_presigned_url(
|
|
1145
|
+
ClientMethod="put_object",
|
|
1146
|
+
Params={
|
|
1147
|
+
"Bucket": bucket_name,
|
|
1148
|
+
"Key": key,
|
|
1149
|
+
"ContentType": content_type
|
|
1150
|
+
},
|
|
1151
|
+
ExpiresIn=expiration
|
|
1152
|
+
)
|
|
1153
|
+
return url
|
|
1154
|
+
except Exception as e:
|
|
1155
|
+
print(f"Presigned URL error: {e}")
|
|
1156
|
+
return None
|
|
1157
|
+
```
|
|
1158
|
+
|
|
1159
|
+
### Presigned POST for File Upload
|
|
1160
|
+
|
|
1161
|
+
```python
|
|
1162
|
+
import boto3
|
|
1163
|
+
|
|
1164
|
+
s3 = boto3.client("s3")
|
|
1165
|
+
|
|
1166
|
+
def create_presigned_post(bucket_name, key, expiration=3600):
|
|
1167
|
+
try:
|
|
1168
|
+
response = s3.generate_presigned_post(
|
|
1169
|
+
Bucket=bucket_name,
|
|
1170
|
+
Key=key,
|
|
1171
|
+
ExpiresIn=expiration
|
|
1172
|
+
)
|
|
1173
|
+
return response
|
|
1174
|
+
except Exception as e:
|
|
1175
|
+
print(f"Presigned POST error: {e}")
|
|
1176
|
+
return None
|
|
1177
|
+
|
|
1178
|
+
# Usage
|
|
1179
|
+
presigned_post = create_presigned_post("my-bucket", "upload.txt")
|
|
1180
|
+
|
|
1181
|
+
# Client uploads using POST with the URL and fields
|
|
1182
|
+
import requests
|
|
1183
|
+
|
|
1184
|
+
with open("file.txt", "rb") as f:
|
|
1185
|
+
files = {"file": f}
|
|
1186
|
+
response = requests.post(
|
|
1187
|
+
presigned_post["url"],
|
|
1188
|
+
data=presigned_post["fields"],
|
|
1189
|
+
files=files
|
|
1190
|
+
)
|
|
1191
|
+
print(f"Upload status: {response.status_code}")
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
### Presigned POST with Conditions
|
|
1195
|
+
|
|
1196
|
+
```python
|
|
1197
|
+
import boto3
|
|
1198
|
+
|
|
1199
|
+
s3 = boto3.client("s3")
|
|
1200
|
+
|
|
1201
|
+
def create_presigned_post_with_conditions(bucket_name, key, max_size=10485760):
|
|
1202
|
+
try:
|
|
1203
|
+
response = s3.generate_presigned_post(
|
|
1204
|
+
Bucket=bucket_name,
|
|
1205
|
+
Key=key,
|
|
1206
|
+
Fields={"acl": "public-read"},
|
|
1207
|
+
Conditions=[
|
|
1208
|
+
{"acl": "public-read"},
|
|
1209
|
+
["content-length-range", 0, max_size] # Max 10 MB
|
|
1210
|
+
],
|
|
1211
|
+
ExpiresIn=3600
|
|
1212
|
+
)
|
|
1213
|
+
return response
|
|
1214
|
+
except Exception as e:
|
|
1215
|
+
print(f"Presigned POST error: {e}")
|
|
1216
|
+
return None
|
|
1217
|
+
```
|
|
1218
|
+
|
|
1219
|
+
---
|
|
1220
|
+
|
|
1221
|
+
## Multipart Upload
|
|
1222
|
+
|
|
1223
|
+
### Automatic Multipart Upload (Recommended)
|
|
1224
|
+
|
|
1225
|
+
```python
|
|
1226
|
+
import boto3
|
|
1227
|
+
from boto3.s3.transfer import TransferConfig
|
|
1228
|
+
|
|
1229
|
+
s3 = boto3.client("s3")
|
|
1230
|
+
|
|
1231
|
+
def multipart_upload(file_path, bucket_name, key):
|
|
1232
|
+
try:
|
|
1233
|
+
# Configure multipart upload
|
|
1234
|
+
config = TransferConfig(
|
|
1235
|
+
multipart_threshold=1024 * 25, # 25 MB
|
|
1236
|
+
max_concurrency=10,
|
|
1237
|
+
multipart_chunksize=1024 * 25, # 25 MB
|
|
1238
|
+
use_threads=True
|
|
1239
|
+
)
|
|
1240
|
+
|
|
1241
|
+
s3.upload_file(
|
|
1242
|
+
file_path,
|
|
1243
|
+
bucket_name,
|
|
1244
|
+
key,
|
|
1245
|
+
Config=config
|
|
1246
|
+
)
|
|
1247
|
+
|
|
1248
|
+
print(f"Multipart upload complete: {key}")
|
|
1249
|
+
except Exception as e:
|
|
1250
|
+
print(f"Upload error: {e}")
|
|
1251
|
+
```
|
|
1252
|
+
|
|
1253
|
+
### Multipart Upload with Progress Callback
|
|
1254
|
+
|
|
1255
|
+
```python
|
|
1256
|
+
import boto3
|
|
1257
|
+
import sys
|
|
1258
|
+
|
|
1259
|
+
s3 = boto3.client("s3")
|
|
1260
|
+
|
|
1261
|
+
class ProgressPercentage:
|
|
1262
|
+
def __init__(self, filename):
|
|
1263
|
+
self._filename = filename
|
|
1264
|
+
self._size = float(os.path.getsize(filename))
|
|
1265
|
+
self._seen_so_far = 0
|
|
1266
|
+
|
|
1267
|
+
def __call__(self, bytes_amount):
|
|
1268
|
+
self._seen_so_far += bytes_amount
|
|
1269
|
+
percentage = (self._seen_so_far / self._size) * 100
|
|
1270
|
+
sys.stdout.write(
|
|
1271
|
+
f"\r{self._filename}: {self._seen_so_far} / {self._size} ({percentage:.2f}%)"
|
|
1272
|
+
)
|
|
1273
|
+
sys.stdout.flush()
|
|
1274
|
+
|
|
1275
|
+
def multipart_upload_with_progress(file_path, bucket_name, key):
|
|
1276
|
+
try:
|
|
1277
|
+
s3.upload_file(
|
|
1278
|
+
file_path,
|
|
1279
|
+
bucket_name,
|
|
1280
|
+
key,
|
|
1281
|
+
Callback=ProgressPercentage(file_path)
|
|
1282
|
+
)
|
|
1283
|
+
print(f"\nUpload complete: {key}")
|
|
1284
|
+
except Exception as e:
|
|
1285
|
+
print(f"Upload error: {e}")
|
|
1286
|
+
```
|
|
1287
|
+
|
|
1288
|
+
### Manual Multipart Upload (Low-Level)
|
|
1289
|
+
|
|
1290
|
+
```python
|
|
1291
|
+
import boto3
|
|
1292
|
+
import os
|
|
1293
|
+
|
|
1294
|
+
s3 = boto3.client("s3")
|
|
1295
|
+
|
|
1296
|
+
def manual_multipart_upload(file_path, bucket_name, key):
|
|
1297
|
+
try:
|
|
1298
|
+
# Step 1: Initiate multipart upload
|
|
1299
|
+
response = s3.create_multipart_upload(
|
|
1300
|
+
Bucket=bucket_name,
|
|
1301
|
+
Key=key
|
|
1302
|
+
)
|
|
1303
|
+
upload_id = response["UploadId"]
|
|
1304
|
+
|
|
1305
|
+
# Step 2: Upload parts
|
|
1306
|
+
parts = []
|
|
1307
|
+
chunk_size = 10 * 1024 * 1024 # 10 MB
|
|
1308
|
+
part_number = 1
|
|
1309
|
+
|
|
1310
|
+
with open(file_path, "rb") as f:
|
|
1311
|
+
while True:
|
|
1312
|
+
data = f.read(chunk_size)
|
|
1313
|
+
if not data:
|
|
1314
|
+
break
|
|
1315
|
+
|
|
1316
|
+
response = s3.upload_part(
|
|
1317
|
+
Bucket=bucket_name,
|
|
1318
|
+
Key=key,
|
|
1319
|
+
PartNumber=part_number,
|
|
1320
|
+
UploadId=upload_id,
|
|
1321
|
+
Body=data
|
|
1322
|
+
)
|
|
1323
|
+
|
|
1324
|
+
parts.append({
|
|
1325
|
+
"PartNumber": part_number,
|
|
1326
|
+
"ETag": response["ETag"]
|
|
1327
|
+
})
|
|
1328
|
+
|
|
1329
|
+
print(f"Uploaded part {part_number}")
|
|
1330
|
+
part_number += 1
|
|
1331
|
+
|
|
1332
|
+
# Step 3: Complete multipart upload
|
|
1333
|
+
s3.complete_multipart_upload(
|
|
1334
|
+
Bucket=bucket_name,
|
|
1335
|
+
Key=key,
|
|
1336
|
+
UploadId=upload_id,
|
|
1337
|
+
MultipartUpload={"Parts": parts}
|
|
1338
|
+
)
|
|
1339
|
+
|
|
1340
|
+
print(f"Multipart upload complete: {key}")
|
|
1341
|
+
except Exception as e:
|
|
1342
|
+
# Abort multipart upload on error
|
|
1343
|
+
s3.abort_multipart_upload(
|
|
1344
|
+
Bucket=bucket_name,
|
|
1345
|
+
Key=key,
|
|
1346
|
+
UploadId=upload_id
|
|
1347
|
+
)
|
|
1348
|
+
print(f"Upload error: {e}")
|
|
1349
|
+
```
|
|
1350
|
+
|
|
1351
|
+
### List In-Progress Multipart Uploads
|
|
1352
|
+
|
|
1353
|
+
```python
|
|
1354
|
+
import boto3
|
|
1355
|
+
|
|
1356
|
+
s3 = boto3.client("s3")
|
|
1357
|
+
|
|
1358
|
+
def list_multipart_uploads(bucket_name):
|
|
1359
|
+
try:
|
|
1360
|
+
response = s3.list_multipart_uploads(Bucket=bucket_name)
|
|
1361
|
+
|
|
1362
|
+
if "Uploads" in response:
|
|
1363
|
+
print("In-progress uploads:")
|
|
1364
|
+
for upload in response["Uploads"]:
|
|
1365
|
+
print(f" • {upload['Key']} (ID: {upload['UploadId']})")
|
|
1366
|
+
else:
|
|
1367
|
+
print("No in-progress uploads")
|
|
1368
|
+
except Exception as e:
|
|
1369
|
+
print(f"List error: {e}")
|
|
1370
|
+
```
|
|
1371
|
+
|
|
1372
|
+
### Abort Multipart Upload
|
|
1373
|
+
|
|
1374
|
+
```python
|
|
1375
|
+
import boto3
|
|
1376
|
+
|
|
1377
|
+
s3 = boto3.client("s3")
|
|
1378
|
+
|
|
1379
|
+
def abort_multipart_upload(bucket_name, key, upload_id):
|
|
1380
|
+
try:
|
|
1381
|
+
s3.abort_multipart_upload(
|
|
1382
|
+
Bucket=bucket_name,
|
|
1383
|
+
Key=key,
|
|
1384
|
+
UploadId=upload_id
|
|
1385
|
+
)
|
|
1386
|
+
print(f"Aborted upload: {key}")
|
|
1387
|
+
except Exception as e:
|
|
1388
|
+
print(f"Abort error: {e}")
|
|
1389
|
+
```
|
|
1390
|
+
|
|
1391
|
+
---
|
|
1392
|
+
|
|
1393
|
+
## Object Tagging
|
|
1394
|
+
|
|
1395
|
+
### Put Object Tags
|
|
1396
|
+
|
|
1397
|
+
```python
|
|
1398
|
+
import boto3
|
|
1399
|
+
|
|
1400
|
+
s3 = boto3.client("s3")
|
|
1401
|
+
|
|
1402
|
+
def tag_object(bucket_name, key, tags):
|
|
1403
|
+
try:
|
|
1404
|
+
s3.put_object_tagging(
|
|
1405
|
+
Bucket=bucket_name,
|
|
1406
|
+
Key=key,
|
|
1407
|
+
Tagging={
|
|
1408
|
+
"TagSet": [
|
|
1409
|
+
{"Key": k, "Value": v} for k, v in tags.items()
|
|
1410
|
+
]
|
|
1411
|
+
}
|
|
1412
|
+
)
|
|
1413
|
+
print(f"Tagged {key}")
|
|
1414
|
+
except Exception as e:
|
|
1415
|
+
print(f"Tagging error: {e}")
|
|
1416
|
+
|
|
1417
|
+
# Usage
|
|
1418
|
+
tag_object("my-bucket", "file.txt", {
|
|
1419
|
+
"Environment": "Production",
|
|
1420
|
+
"Department": "Engineering"
|
|
1421
|
+
})
|
|
1422
|
+
```
|
|
1423
|
+
|
|
1424
|
+
### Get Object Tags
|
|
1425
|
+
|
|
1426
|
+
```python
|
|
1427
|
+
import boto3
|
|
1428
|
+
|
|
1429
|
+
s3 = boto3.client("s3")
|
|
1430
|
+
|
|
1431
|
+
def get_object_tags(bucket_name, key):
|
|
1432
|
+
try:
|
|
1433
|
+
response = s3.get_object_tagging(Bucket=bucket_name, Key=key)
|
|
1434
|
+
|
|
1435
|
+
print("Tags:")
|
|
1436
|
+
for tag in response["TagSet"]:
|
|
1437
|
+
print(f" {tag['Key']}: {tag['Value']}")
|
|
1438
|
+
|
|
1439
|
+
return response["TagSet"]
|
|
1440
|
+
except Exception as e:
|
|
1441
|
+
print(f"Get tags error: {e}")
|
|
1442
|
+
return None
|
|
1443
|
+
```
|
|
1444
|
+
|
|
1445
|
+
### Delete Object Tags
|
|
1446
|
+
|
|
1447
|
+
```python
|
|
1448
|
+
import boto3
|
|
1449
|
+
|
|
1450
|
+
s3 = boto3.client("s3")
|
|
1451
|
+
|
|
1452
|
+
def delete_object_tags(bucket_name, key):
|
|
1453
|
+
try:
|
|
1454
|
+
s3.delete_object_tagging(Bucket=bucket_name, Key=key)
|
|
1455
|
+
print(f"Deleted tags from {key}")
|
|
1456
|
+
except Exception as e:
|
|
1457
|
+
print(f"Delete tags error: {e}")
|
|
1458
|
+
```
|
|
1459
|
+
|
|
1460
|
+
---
|
|
1461
|
+
|
|
1462
|
+
## Bucket Configuration
|
|
1463
|
+
|
|
1464
|
+
### Enable Versioning
|
|
1465
|
+
|
|
1466
|
+
```python
|
|
1467
|
+
import boto3
|
|
1468
|
+
|
|
1469
|
+
s3 = boto3.client("s3")
|
|
1470
|
+
|
|
1471
|
+
def enable_versioning(bucket_name):
|
|
1472
|
+
try:
|
|
1473
|
+
s3.put_bucket_versioning(
|
|
1474
|
+
Bucket=bucket_name,
|
|
1475
|
+
VersioningConfiguration={"Status": "Enabled"}
|
|
1476
|
+
)
|
|
1477
|
+
print(f"Versioning enabled for {bucket_name}")
|
|
1478
|
+
except Exception as e:
|
|
1479
|
+
print(f"Versioning error: {e}")
|
|
1480
|
+
```
|
|
1481
|
+
|
|
1482
|
+
### Get Versioning Status
|
|
1483
|
+
|
|
1484
|
+
```python
|
|
1485
|
+
import boto3
|
|
1486
|
+
|
|
1487
|
+
s3 = boto3.client("s3")
|
|
1488
|
+
|
|
1489
|
+
def get_versioning_status(bucket_name):
|
|
1490
|
+
try:
|
|
1491
|
+
response = s3.get_bucket_versioning(Bucket=bucket_name)
|
|
1492
|
+
status = response.get("Status", "Disabled")
|
|
1493
|
+
print(f"Versioning status: {status}")
|
|
1494
|
+
return status
|
|
1495
|
+
except Exception as e:
|
|
1496
|
+
print(f"Error: {e}")
|
|
1497
|
+
```
|
|
1498
|
+
|
|
1499
|
+
### Set Bucket CORS
|
|
1500
|
+
|
|
1501
|
+
```python
|
|
1502
|
+
import boto3
|
|
1503
|
+
|
|
1504
|
+
s3 = boto3.client("s3")
|
|
1505
|
+
|
|
1506
|
+
def set_bucket_cors(bucket_name):
|
|
1507
|
+
try:
|
|
1508
|
+
cors_configuration = {
|
|
1509
|
+
"CORSRules": [
|
|
1510
|
+
{
|
|
1511
|
+
"AllowedHeaders": ["*"],
|
|
1512
|
+
"AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
|
|
1513
|
+
"AllowedOrigins": ["*"],
|
|
1514
|
+
"ExposeHeaders": ["ETag"],
|
|
1515
|
+
"MaxAgeSeconds": 3000
|
|
1516
|
+
}
|
|
1517
|
+
]
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
s3.put_bucket_cors(
|
|
1521
|
+
Bucket=bucket_name,
|
|
1522
|
+
CORSConfiguration=cors_configuration
|
|
1523
|
+
)
|
|
1524
|
+
|
|
1525
|
+
print("CORS configured")
|
|
1526
|
+
except Exception as e:
|
|
1527
|
+
print(f"CORS error: {e}")
|
|
1528
|
+
```
|
|
1529
|
+
|
|
1530
|
+
### Get Bucket CORS
|
|
1531
|
+
|
|
1532
|
+
```python
|
|
1533
|
+
import boto3
|
|
1534
|
+
|
|
1535
|
+
s3 = boto3.client("s3")
|
|
1536
|
+
|
|
1537
|
+
def get_bucket_cors(bucket_name):
|
|
1538
|
+
try:
|
|
1539
|
+
response = s3.get_bucket_cors(Bucket=bucket_name)
|
|
1540
|
+
print("CORS Rules:")
|
|
1541
|
+
print(response["CORSRules"])
|
|
1542
|
+
return response["CORSRules"]
|
|
1543
|
+
except Exception as e:
|
|
1544
|
+
print(f"Get CORS error: {e}")
|
|
1545
|
+
return None
|
|
1546
|
+
```
|
|
1547
|
+
|
|
1548
|
+
### Delete Bucket CORS
|
|
1549
|
+
|
|
1550
|
+
```python
|
|
1551
|
+
import boto3
|
|
1552
|
+
|
|
1553
|
+
s3 = boto3.client("s3")
|
|
1554
|
+
|
|
1555
|
+
def delete_bucket_cors(bucket_name):
|
|
1556
|
+
try:
|
|
1557
|
+
s3.delete_bucket_cors(Bucket=bucket_name)
|
|
1558
|
+
print("CORS configuration deleted")
|
|
1559
|
+
except Exception as e:
|
|
1560
|
+
print(f"Delete CORS error: {e}")
|
|
1561
|
+
```
|
|
1562
|
+
|
|
1563
|
+
### Set Bucket Encryption
|
|
1564
|
+
|
|
1565
|
+
```python
|
|
1566
|
+
import boto3
|
|
1567
|
+
|
|
1568
|
+
s3 = boto3.client("s3")
|
|
1569
|
+
|
|
1570
|
+
def set_bucket_encryption(bucket_name):
|
|
1571
|
+
try:
|
|
1572
|
+
s3.put_bucket_encryption(
|
|
1573
|
+
Bucket=bucket_name,
|
|
1574
|
+
ServerSideEncryptionConfiguration={
|
|
1575
|
+
"Rules": [
|
|
1576
|
+
{
|
|
1577
|
+
"ApplyServerSideEncryptionByDefault": {
|
|
1578
|
+
"SSEAlgorithm": "AES256"
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
]
|
|
1582
|
+
}
|
|
1583
|
+
)
|
|
1584
|
+
print("Encryption enabled")
|
|
1585
|
+
except Exception as e:
|
|
1586
|
+
print(f"Encryption error: {e}")
|
|
1587
|
+
```
|
|
1588
|
+
|
|
1589
|
+
### Set Bucket Lifecycle Policy
|
|
1590
|
+
|
|
1591
|
+
```python
|
|
1592
|
+
import boto3
|
|
1593
|
+
|
|
1594
|
+
s3 = boto3.client("s3")
|
|
1595
|
+
|
|
1596
|
+
def set_lifecycle_policy(bucket_name):
|
|
1597
|
+
try:
|
|
1598
|
+
s3.put_bucket_lifecycle_configuration(
|
|
1599
|
+
Bucket=bucket_name,
|
|
1600
|
+
LifecycleConfiguration={
|
|
1601
|
+
"Rules": [
|
|
1602
|
+
{
|
|
1603
|
+
"Id": "DeleteOldObjects",
|
|
1604
|
+
"Status": "Enabled",
|
|
1605
|
+
"Expiration": {"Days": 90},
|
|
1606
|
+
"Filter": {"Prefix": "logs/"}
|
|
1607
|
+
}
|
|
1608
|
+
]
|
|
1609
|
+
}
|
|
1610
|
+
)
|
|
1611
|
+
print("Lifecycle policy configured")
|
|
1612
|
+
except Exception as e:
|
|
1613
|
+
print(f"Lifecycle error: {e}")
|
|
1614
|
+
```
|
|
1615
|
+
|
|
1616
|
+
---
|
|
1617
|
+
|
|
1618
|
+
## Waiters
|
|
1619
|
+
|
|
1620
|
+
### Wait Until Object Exists
|
|
1621
|
+
|
|
1622
|
+
```python
|
|
1623
|
+
import boto3
|
|
1624
|
+
|
|
1625
|
+
s3 = boto3.client("s3")
|
|
1626
|
+
|
|
1627
|
+
def wait_for_object(bucket_name, key):
|
|
1628
|
+
try:
|
|
1629
|
+
waiter = s3.get_waiter("object_exists")
|
|
1630
|
+
waiter.wait(Bucket=bucket_name, Key=key)
|
|
1631
|
+
print(f"Object exists: {key}")
|
|
1632
|
+
except Exception as e:
|
|
1633
|
+
print(f"Wait error: {e}")
|
|
1634
|
+
```
|
|
1635
|
+
|
|
1636
|
+
### Wait Until Object Not Exists
|
|
1637
|
+
|
|
1638
|
+
```python
|
|
1639
|
+
import boto3
|
|
1640
|
+
|
|
1641
|
+
s3 = boto3.client("s3")
|
|
1642
|
+
|
|
1643
|
+
def wait_for_object_deletion(bucket_name, key):
|
|
1644
|
+
try:
|
|
1645
|
+
waiter = s3.get_waiter("object_not_exists")
|
|
1646
|
+
waiter.wait(Bucket=bucket_name, Key=key)
|
|
1647
|
+
print(f"Object deleted: {key}")
|
|
1648
|
+
except Exception as e:
|
|
1649
|
+
print(f"Wait error: {e}")
|
|
1650
|
+
```
|
|
1651
|
+
|
|
1652
|
+
### Wait Until Bucket Exists
|
|
1653
|
+
|
|
1654
|
+
```python
|
|
1655
|
+
import boto3
|
|
1656
|
+
|
|
1657
|
+
s3 = boto3.client("s3")
|
|
1658
|
+
|
|
1659
|
+
def wait_for_bucket(bucket_name):
|
|
1660
|
+
try:
|
|
1661
|
+
waiter = s3.get_waiter("bucket_exists")
|
|
1662
|
+
waiter.wait(Bucket=bucket_name)
|
|
1663
|
+
print(f"Bucket exists: {bucket_name}")
|
|
1664
|
+
except Exception as e:
|
|
1665
|
+
print(f"Wait error: {e}")
|
|
1666
|
+
```
|
|
1667
|
+
|
|
1668
|
+
---
|
|
1669
|
+
|
|
1670
|
+
## Error Handling
|
|
1671
|
+
|
|
1672
|
+
### Common Error Types
|
|
1673
|
+
|
|
1674
|
+
```python
|
|
1675
|
+
import boto3
|
|
1676
|
+
from botocore.exceptions import ClientError, NoCredentialsError
|
|
1677
|
+
|
|
1678
|
+
s3 = boto3.client("s3")
|
|
1679
|
+
|
|
1680
|
+
def handle_errors(bucket_name, key):
|
|
1681
|
+
try:
|
|
1682
|
+
response = s3.get_object(Bucket=bucket_name, Key=key)
|
|
1683
|
+
return response["Body"].read()
|
|
1684
|
+
except NoCredentialsError:
|
|
1685
|
+
print("No AWS credentials found")
|
|
1686
|
+
except ClientError as e:
|
|
1687
|
+
error_code = e.response["Error"]["Code"]
|
|
1688
|
+
|
|
1689
|
+
if error_code == "NoSuchKey":
|
|
1690
|
+
print("Object not found")
|
|
1691
|
+
elif error_code == "NoSuchBucket":
|
|
1692
|
+
print("Bucket not found")
|
|
1693
|
+
elif error_code == "AccessDenied":
|
|
1694
|
+
print("Access denied")
|
|
1695
|
+
elif error_code == "InvalidAccessKeyId":
|
|
1696
|
+
print("Invalid credentials")
|
|
1697
|
+
else:
|
|
1698
|
+
print(f"Error: {error_code}")
|
|
1699
|
+
except Exception as e:
|
|
1700
|
+
print(f"Unknown error: {e}")
|
|
1701
|
+
```
|
|
1702
|
+
|
|
1703
|
+
### Retry Logic with Backoff
|
|
1704
|
+
|
|
1705
|
+
```python
|
|
1706
|
+
import boto3
|
|
1707
|
+
import time
|
|
1708
|
+
from botocore.exceptions import ClientError
|
|
1709
|
+
|
|
1710
|
+
s3 = boto3.client("s3")
|
|
1711
|
+
|
|
1712
|
+
def download_with_retry(bucket_name, key, max_retries=3):
|
|
1713
|
+
for attempt in range(max_retries):
|
|
1714
|
+
try:
|
|
1715
|
+
response = s3.get_object(Bucket=bucket_name, Key=key)
|
|
1716
|
+
return response["Body"].read()
|
|
1717
|
+
except ClientError as e:
|
|
1718
|
+
if attempt < max_retries - 1:
|
|
1719
|
+
wait_time = 2 ** attempt # Exponential backoff
|
|
1720
|
+
print(f"Attempt {attempt + 1} failed, retrying in {wait_time}s...")
|
|
1721
|
+
time.sleep(wait_time)
|
|
1722
|
+
else:
|
|
1723
|
+
raise
|
|
1724
|
+
```
|
|
1725
|
+
|
|
1726
|
+
### Custom Retry Configuration
|
|
1727
|
+
|
|
1728
|
+
```python
|
|
1729
|
+
import boto3
|
|
1730
|
+
from botocore.config import Config
|
|
1731
|
+
|
|
1732
|
+
# Configure custom retry settings
|
|
1733
|
+
config = Config(
|
|
1734
|
+
retries={
|
|
1735
|
+
"max_attempts": 10,
|
|
1736
|
+
"mode": "adaptive"
|
|
1737
|
+
}
|
|
1738
|
+
)
|
|
1739
|
+
|
|
1740
|
+
s3 = boto3.client("s3", config=config)
|
|
1741
|
+
```
|
|
1742
|
+
|
|
1743
|
+
---
|
|
1744
|
+
|
|
1745
|
+
## Complete Example: Full S3 Operations
|
|
1746
|
+
|
|
1747
|
+
```python
|
|
1748
|
+
import boto3
|
|
1749
|
+
from botocore.exceptions import ClientError
|
|
1750
|
+
import json
|
|
1751
|
+
|
|
1752
|
+
def main():
|
|
1753
|
+
s3 = boto3.client("s3", region_name="us-east-1")
|
|
1754
|
+
bucket_name = f"my-test-bucket-{int(time.time())}"
|
|
1755
|
+
|
|
1756
|
+
try:
|
|
1757
|
+
# 1. Create bucket
|
|
1758
|
+
print("Creating bucket...")
|
|
1759
|
+
s3.create_bucket(Bucket=bucket_name)
|
|
1760
|
+
|
|
1761
|
+
# 2. Upload object
|
|
1762
|
+
print("Uploading object...")
|
|
1763
|
+
s3.put_object(
|
|
1764
|
+
Bucket=bucket_name,
|
|
1765
|
+
Key="test-file.txt",
|
|
1766
|
+
Body=b"Hello, S3!",
|
|
1767
|
+
ContentType="text/plain"
|
|
1768
|
+
)
|
|
1769
|
+
|
|
1770
|
+
# 3. Generate presigned URL
|
|
1771
|
+
print("Generating presigned URL...")
|
|
1772
|
+
url = s3.generate_presigned_url(
|
|
1773
|
+
ClientMethod="get_object",
|
|
1774
|
+
Params={"Bucket": bucket_name, "Key": "test-file.txt"},
|
|
1775
|
+
ExpiresIn=3600
|
|
1776
|
+
)
|
|
1777
|
+
print(f"Presigned URL: {url}")
|
|
1778
|
+
|
|
1779
|
+
# 4. Download object
|
|
1780
|
+
print("Downloading object...")
|
|
1781
|
+
response = s3.get_object(Bucket=bucket_name, Key="test-file.txt")
|
|
1782
|
+
content = response["Body"].read().decode("utf-8")
|
|
1783
|
+
print(f"Content: {content}")
|
|
1784
|
+
|
|
1785
|
+
# 5. List objects
|
|
1786
|
+
print("Listing objects...")
|
|
1787
|
+
response = s3.list_objects_v2(Bucket=bucket_name)
|
|
1788
|
+
for obj in response.get("Contents", []):
|
|
1789
|
+
print(f" • {obj['Key']}")
|
|
1790
|
+
|
|
1791
|
+
# 6. Delete object
|
|
1792
|
+
print("Deleting object...")
|
|
1793
|
+
s3.delete_object(Bucket=bucket_name, Key="test-file.txt")
|
|
1794
|
+
|
|
1795
|
+
# 7. Delete bucket
|
|
1796
|
+
print("Deleting bucket...")
|
|
1797
|
+
s3.delete_bucket(Bucket=bucket_name)
|
|
1798
|
+
|
|
1799
|
+
print("Complete!")
|
|
1800
|
+
|
|
1801
|
+
except ClientError as e:
|
|
1802
|
+
print(f"Error: {e}")
|
|
1803
|
+
|
|
1804
|
+
if __name__ == "__main__":
|
|
1805
|
+
import time
|
|
1806
|
+
main()
|
|
1807
|
+
```
|