0
# Attachments
1
2
File attachment handling including adding, retrieving, and managing binary data associated with database entries.
3
4
## Capabilities
5
6
### Binary Data Management
7
8
Manage binary data storage in the database for file attachments.
9
10
```python { .api }
11
def add_binary(data, compressed=True, protected=True):
12
"""
13
Add binary data to the database.
14
15
Parameters:
16
- data (bytes): Binary data to store
17
- compressed (bool): Whether to compress the data (default True)
18
- protected (bool): Whether to protect the data in memory (default True)
19
20
Returns:
21
int: Binary ID that can be used to reference this data
22
"""
23
24
def delete_binary(id):
25
"""
26
Delete binary data and all references to it.
27
28
Parameters:
29
- id (int): Binary ID to delete
30
31
Note: This will remove the binary data and all attachment references to it
32
"""
33
```
34
35
**Usage Examples:**
36
37
```python
38
from pykeepass import PyKeePass
39
40
kp = PyKeePass('database.kdbx', password='secret')
41
42
# Read file and add as binary data
43
with open('document.pdf', 'rb') as f:
44
binary_data = f.read()
45
46
# Add binary data to database
47
binary_id = kp.add_binary(binary_data, compressed=True, protected=True)
48
print(f"Binary stored with ID: {binary_id}")
49
50
# Delete binary data when no longer needed
51
kp.delete_binary(binary_id)
52
53
kp.save()
54
```
55
56
### Attachment Search and Retrieval
57
58
Find attachments across the database with flexible search criteria.
59
60
```python { .api }
61
def find_attachments(**kwargs):
62
"""
63
Find attachments with flexible criteria.
64
65
Parameters:
66
- filename (str, optional): Match filename
67
- id (int, optional): Match binary ID
68
- element (Element, optional): Match parent element
69
- regex (bool): Use regex matching for filename
70
- flags (int, optional): Regex flags
71
- first (bool): Return only first match
72
73
Returns:
74
list or Attachment: List of matching attachments, or single attachment if first=True
75
"""
76
```
77
78
**Usage Examples:**
79
80
```python
81
import re
82
83
# Find all attachments
84
all_attachments = kp.find_attachments()
85
86
# Find by filename
87
pdf_attachments = kp.find_attachments(filename='document.pdf')
88
image_attachments = kp.find_attachments(filename=r'.*\.(jpg|png|gif)$', regex=True)
89
90
# Find by binary ID
91
attachment = kp.find_attachments(id=123, first=True)
92
93
# Case-insensitive filename search
94
docs = kp.find_attachments(filename='readme', regex=True, flags=re.IGNORECASE)
95
96
# Get first attachment matching criteria
97
first_pdf = kp.find_attachments(filename=r'.*\.pdf$', regex=True, first=True)
98
```
99
100
### Entry Attachment Management
101
102
Manage attachments associated with specific entries.
103
104
```python { .api }
105
# Entry methods for attachment management
106
def add_attachment(id, filename):
107
"""
108
Add an attachment to this entry.
109
110
Parameters:
111
- id (int): Binary ID of stored data
112
- filename (str): Display filename for the attachment
113
114
Returns:
115
Attachment: The created attachment object
116
"""
117
118
def delete_attachment(attachment):
119
"""
120
Remove an attachment from this entry.
121
122
Parameters:
123
- attachment (Attachment): Attachment object to remove
124
125
Note: This removes the attachment reference but not the binary data
126
"""
127
```
128
129
**Usage Examples:**
130
131
```python
132
# Find entry to attach file to
133
entry = kp.find_entries_by_title('Important Document', first=True)
134
135
# Read and store binary data
136
with open('contract.pdf', 'rb') as f:
137
binary_data = f.read()
138
139
binary_id = kp.add_binary(binary_data)
140
141
# Attach to entry
142
attachment = entry.add_attachment(binary_id, 'contract.pdf')
143
print(f"Attached {attachment.filename} to {entry.title}")
144
145
# Remove attachment from entry
146
entry.delete_attachment(attachment)
147
148
kp.save()
149
```
150
151
### Attachment Class and Properties
152
153
The Attachment class represents file attachments linked to entries.
154
155
```python { .api }
156
class Attachment:
157
def __init__(element=None, kp=None, id=None, filename=None):
158
"""Create a new Attachment object."""
159
160
@property
161
def id() -> int:
162
"""Binary reference ID"""
163
164
@property
165
def filename() -> str:
166
"""Attachment filename"""
167
168
@property
169
def entry() -> Entry:
170
"""Parent entry that owns this attachment"""
171
172
@property
173
def binary() -> bytes:
174
"""Binary data content"""
175
176
@property
177
def data() -> bytes:
178
"""Alias for binary property - binary data content"""
179
180
def delete():
181
"""Remove this attachment from its parent entry."""
182
```
183
184
**Usage Examples:**
185
186
```python
187
# Access attachment properties
188
entry = kp.find_entries_by_title('Document Entry', first=True)
189
190
for attachment in entry.attachments:
191
print(f"Filename: {attachment.filename}")
192
print(f"Binary ID: {attachment.id}")
193
print(f"Data size: {len(attachment.binary)} bytes")
194
print(f"Parent entry: {attachment.entry.title}")
195
196
# Save attachment to file
197
with open(f"exported_{attachment.filename}", 'wb') as f:
198
f.write(attachment.data) # or attachment.binary
199
```
200
201
### Complete Attachment Workflow
202
203
Examples showing complete workflows for working with attachments.
204
205
**Adding Files as Attachments:**
206
207
```python
208
from pykeepass import PyKeePass
209
import os
210
211
kp = PyKeePass('database.kdbx', password='secret')
212
213
# Find or create entry
214
entry = kp.find_entries_by_title('Project Files', first=True)
215
if not entry:
216
group = kp.find_groups_by_name('Work', first=True)
217
entry = kp.add_entry(group, 'Project Files', 'username', 'password')
218
219
# Add multiple files
220
files_to_attach = ['spec.pdf', 'diagram.png', 'notes.txt']
221
222
for filepath in files_to_attach:
223
if os.path.exists(filepath):
224
# Read file
225
with open(filepath, 'rb') as f:
226
binary_data = f.read()
227
228
# Store in database
229
binary_id = kp.add_binary(binary_data, compressed=True, protected=True)
230
231
# Attach to entry
232
filename = os.path.basename(filepath)
233
attachment = entry.add_attachment(binary_id, filename)
234
print(f"Attached: {filename} ({len(binary_data)} bytes)")
235
236
kp.save()
237
```
238
239
**Extracting Attachments:**
240
241
```python
242
# Extract all attachments from database
243
output_dir = 'extracted_attachments'
244
os.makedirs(output_dir, exist_ok=True)
245
246
all_attachments = kp.find_attachments()
247
248
for attachment in all_attachments:
249
# Create safe filename
250
safe_filename = attachment.filename.replace('/', '_').replace('\\', '_')
251
output_path = os.path.join(output_dir, safe_filename)
252
253
# Handle filename conflicts
254
counter = 1
255
base_path = output_path
256
while os.path.exists(output_path):
257
name, ext = os.path.splitext(base_path)
258
output_path = f"{name}_{counter}{ext}"
259
counter += 1
260
261
# Write attachment data
262
with open(output_path, 'wb') as f:
263
f.write(attachment.binary)
264
265
print(f"Extracted: {attachment.filename} -> {output_path}")
266
print(f" From entry: {attachment.entry.title}")
267
print(f" Size: {len(attachment.binary)} bytes")
268
```
269
270
**Managing Attachment Storage:**
271
272
```python
273
# Clean up unused binary data
274
used_binary_ids = set()
275
276
# Collect all binary IDs in use
277
for attachment in kp.find_attachments():
278
used_binary_ids.add(attachment.id)
279
280
# Find unused binaries (this requires access to internal binary storage)
281
all_binary_ids = set(range(len(kp.binaries)))
282
unused_binary_ids = all_binary_ids - used_binary_ids
283
284
# Remove unused binaries
285
for binary_id in unused_binary_ids:
286
try:
287
kp.delete_binary(binary_id)
288
print(f"Deleted unused binary ID: {binary_id}")
289
except Exception as e:
290
print(f"Could not delete binary ID {binary_id}: {e}")
291
292
kp.save()
293
294
# Report attachment statistics
295
total_attachments = len(kp.find_attachments())
296
total_size = sum(len(attachment.binary) for attachment in kp.find_attachments())
297
298
print(f"Database contains {total_attachments} attachments")
299
print(f"Total attachment size: {total_size / 1024:.1f} KB")
300
```
301
302
**Attachment Validation:**
303
304
```python
305
# Validate all attachments
306
invalid_attachments = []
307
308
for attachment in kp.find_attachments():
309
try:
310
# Test access to binary data
311
data = attachment.binary
312
if not data or len(data) == 0:
313
invalid_attachments.append((attachment, "Empty data"))
314
315
# Validate filename
316
if not attachment.filename or attachment.filename.strip() == '':
317
invalid_attachments.append((attachment, "Invalid filename"))
318
319
# Check parent entry exists
320
if not attachment.entry:
321
invalid_attachments.append((attachment, "No parent entry"))
322
323
except Exception as e:
324
invalid_attachments.append((attachment, f"Access error: {e}"))
325
326
# Report issues
327
if invalid_attachments:
328
print("Invalid attachments found:")
329
for attachment, issue in invalid_attachments:
330
print(f" {attachment.filename}: {issue}")
331
else:
332
print("All attachments are valid")
333
```