0
# Media Upload and Download
1
2
The media handling functionality provides robust support for uploading and downloading files to Google APIs with features like resumable transfers, progress tracking, and chunked processing for large files.
3
4
## Capabilities
5
6
### Media Upload Classes
7
8
Upload files and data to Google APIs with various source types and transfer options.
9
10
```python { .api }
11
class MediaUpload:
12
"""Abstract base class for media uploads."""
13
14
def chunksize(self):
15
"""
16
Get the chunk size for resumable uploads.
17
18
Returns:
19
int: Chunk size in bytes
20
"""
21
22
def mimetype(self):
23
"""
24
Get the MIME type of the media.
25
26
Returns:
27
str: MIME type string
28
"""
29
30
def size(self):
31
"""
32
Get the total size of the media.
33
34
Returns:
35
int: Size in bytes, or None if unknown
36
"""
37
38
def resumable(self):
39
"""
40
Check if the upload supports resumable transfers.
41
42
Returns:
43
bool: True if resumable, False otherwise
44
"""
45
46
def getbytes(self, begin, end):
47
"""
48
Get a range of bytes from the media.
49
50
Args:
51
begin (int): Starting byte position
52
end (int): Ending byte position
53
54
Returns:
55
bytes: The requested byte range
56
"""
57
58
class MediaFileUpload(MediaUpload):
59
"""Upload a file from the local filesystem."""
60
61
def __init__(self, filename, mimetype=None, chunksize=DEFAULT_CHUNK_SIZE,
62
resumable=False):
63
"""
64
Initialize a file upload.
65
66
Args:
67
filename (str): Path to the file to upload
68
mimetype (str, optional): MIME type of the file (auto-detected if None)
69
chunksize (int): Size of upload chunks in bytes (default: 1MB)
70
resumable (bool): Whether the upload should be resumable (default: False)
71
72
Raises:
73
FileNotFoundError: When the specified file does not exist
74
ValueError: When chunksize is invalid for resumable uploads
75
"""
76
77
class MediaInMemoryUpload(MediaUpload):
78
"""Upload data from memory (bytes or string)."""
79
80
def __init__(self, body, mimetype='application/octet-stream',
81
chunksize=DEFAULT_CHUNK_SIZE, resumable=False):
82
"""
83
Initialize an in-memory upload.
84
85
Args:
86
body (bytes or str): Data to upload
87
mimetype (str): MIME type of the data
88
chunksize (int): Size of upload chunks in bytes (default: 1MB)
89
resumable (bool): Whether the upload should be resumable (default: False)
90
91
Raises:
92
ValueError: When chunksize is invalid for resumable uploads
93
"""
94
95
class MediaIoBaseUpload(MediaUpload):
96
"""Upload data from a file-like object (IOBase)."""
97
98
def __init__(self, fd, mimetype='application/octet-stream',
99
chunksize=DEFAULT_CHUNK_SIZE, resumable=False):
100
"""
101
Initialize an IOBase upload.
102
103
Args:
104
fd (IOBase): File-like object to read data from
105
mimetype (str): MIME type of the data
106
chunksize (int): Size of upload chunks in bytes (default: 1MB)
107
resumable (bool): Whether the upload should be resumable (default: False)
108
109
Raises:
110
ValueError: When chunksize is invalid for resumable uploads
111
"""
112
```
113
114
### Media Download Classes
115
116
Download files and data from Google APIs with progress tracking and chunked processing.
117
118
```python { .api }
119
class MediaDownloadProgress:
120
"""Tracks the progress of a media download."""
121
122
def __init__(self, resumable_progress, total_size):
123
"""
124
Initialize download progress tracking.
125
126
Args:
127
resumable_progress (int): Bytes downloaded so far
128
total_size (int): Total size in bytes, or None if unknown
129
"""
130
131
@property
132
def resumable_progress(self):
133
"""
134
Get the number of bytes downloaded.
135
136
Returns:
137
int: Bytes downloaded so far
138
"""
139
140
@property
141
def total_size(self):
142
"""
143
Get the total download size.
144
145
Returns:
146
int: Total size in bytes, or None if unknown
147
"""
148
149
def progress(self):
150
"""
151
Get the download progress as a percentage.
152
153
Returns:
154
float: Progress percentage (0.0 to 1.0), or None if total size unknown
155
"""
156
157
class MediaIoBaseDownload:
158
"""Download media to a file-like object."""
159
160
def __init__(self, fd, request, chunksize=DEFAULT_CHUNK_SIZE):
161
"""
162
Initialize a media download.
163
164
Args:
165
fd (IOBase): File-like object to write downloaded data
166
request (HttpRequest): HTTP request for the media download
167
chunksize (int): Size of download chunks in bytes (default: 1MB)
168
"""
169
170
def next_chunk(self, num_retries=0):
171
"""
172
Download the next chunk of media.
173
174
Args:
175
num_retries (int): Number of retry attempts on failure
176
177
Returns:
178
tuple: (MediaDownloadProgress, bool) - progress object and completion status
179
180
Raises:
181
HttpError: When the download request fails
182
"""
183
```
184
185
### Upload Progress Tracking
186
187
```python { .api }
188
class MediaUploadProgress:
189
"""Tracks the progress of a resumable media upload."""
190
191
def __init__(self, resumable_progress, total_size):
192
"""
193
Initialize upload progress tracking.
194
195
Args:
196
resumable_progress (int): Bytes uploaded so far
197
total_size (int): Total size in bytes
198
"""
199
200
@property
201
def resumable_progress(self):
202
"""
203
Get the number of bytes uploaded.
204
205
Returns:
206
int: Bytes uploaded so far
207
"""
208
209
@property
210
def total_size(self):
211
"""
212
Get the total upload size.
213
214
Returns:
215
int: Total size in bytes
216
"""
217
218
def progress(self):
219
"""
220
Get the upload progress as a percentage.
221
222
Returns:
223
float: Progress percentage (0.0 to 1.0)
224
"""
225
```
226
227
## Usage Examples
228
229
### File Upload
230
231
```python
232
from googleapiclient import discovery
233
from googleapiclient.http import MediaFileUpload
234
235
# Build Drive service
236
service = discovery.build('drive', 'v3', credentials=credentials)
237
238
# Upload a file
239
file_metadata = {
240
'name': 'my-document.pdf',
241
'parents': ['folder_id'] # Optional: specify parent folder
242
}
243
244
media = MediaFileUpload(
245
'local-document.pdf',
246
mimetype='application/pdf',
247
resumable=True
248
)
249
250
file = service.files().create(
251
body=file_metadata,
252
media_body=media,
253
fields='id'
254
).execute()
255
256
print(f'File ID: {file.get("id")}')
257
```
258
259
### Resumable Upload with Progress Tracking
260
261
```python
262
from googleapiclient.http import MediaFileUpload
263
from googleapiclient.errors import HttpError
264
import time
265
266
def upload_with_progress(service, filename, file_metadata):
267
"""Upload file with progress tracking and resume capability."""
268
269
media = MediaFileUpload(
270
filename,
271
resumable=True,
272
chunksize=1024*1024 # 1MB chunks
273
)
274
275
request = service.files().create(
276
body=file_metadata,
277
media_body=media
278
)
279
280
response = None
281
while response is None:
282
try:
283
status, response = request.next_chunk()
284
if status:
285
progress = int(status.progress() * 100)
286
print(f"Upload progress: {progress}%")
287
except HttpError as error:
288
if error.resp.status in [500, 502, 503, 504]:
289
# Recoverable error, wait and retry
290
time.sleep(5)
291
continue
292
else:
293
raise
294
295
return response
296
297
# Use the function
298
file_metadata = {'name': 'large-file.zip'}
299
result = upload_with_progress(service, 'large-file.zip', file_metadata)
300
print(f'Upload completed. File ID: {result.get("id")}')
301
```
302
303
### In-Memory Upload
304
305
```python
306
from googleapiclient.http import MediaInMemoryUpload
307
import json
308
309
# Upload JSON data
310
data = {'key': 'value', 'numbers': [1, 2, 3]}
311
json_string = json.dumps(data)
312
313
media = MediaInMemoryUpload(
314
json_string.encode('utf-8'),
315
mimetype='application/json'
316
)
317
318
file_metadata = {'name': 'data.json'}
319
file = service.files().create(
320
body=file_metadata,
321
media_body=media
322
).execute()
323
```
324
325
### Stream Upload
326
327
```python
328
from googleapiclient.http import MediaIoBaseUpload
329
import io
330
331
# Upload from a file-like object
332
data_stream = io.BytesIO(b'Hello, world! This is streaming data.')
333
334
media = MediaIoBaseUpload(
335
data_stream,
336
mimetype='text/plain',
337
resumable=True
338
)
339
340
file_metadata = {'name': 'stream-data.txt'}
341
file = service.files().create(
342
body=file_metadata,
343
media_body=media
344
).execute()
345
```
346
347
### File Download
348
349
```python
350
from googleapiclient.http import MediaIoBaseDownload
351
import io
352
353
# Download a file
354
request = service.files().get_media(fileId='file_id')
355
file_io = io.BytesIO()
356
downloader = MediaIoBaseDownload(file_io, request)
357
358
done = False
359
while done is False:
360
status, done = downloader.next_chunk()
361
if status:
362
progress = int(status.progress() * 100)
363
print(f"Download progress: {progress}%")
364
365
# File is now in file_io
366
file_content = file_io.getvalue()
367
print(f"Downloaded {len(file_content)} bytes")
368
```
369
370
### Download to File
371
372
```python
373
from googleapiclient.http import MediaIoBaseDownload
374
375
def download_file(service, file_id, local_filename):
376
"""Download a file to local filesystem."""
377
378
request = service.files().get_media(fileId=file_id)
379
380
with open(local_filename, 'wb') as f:
381
downloader = MediaIoBaseDownload(f, request)
382
done = False
383
384
while done is False:
385
status, done = downloader.next_chunk()
386
if status:
387
progress = int(status.progress() * 100)
388
print(f"Download {progress}% complete")
389
390
print(f"Download completed: {local_filename}")
391
392
# Use the function
393
download_file(service, 'file_id_here', 'downloaded-file.pdf')
394
```
395
396
### Batch Upload
397
398
```python
399
from googleapiclient import http
400
from googleapiclient.http import MediaFileUpload
401
402
def batch_upload_callback(request_id, response, exception):
403
if exception is not None:
404
print(f'Upload {request_id} failed: {exception}')
405
else:
406
print(f'Upload {request_id} completed: {response.get("id")}')
407
408
# Create batch for multiple uploads
409
batch = http.BatchHttpRequest(callback=batch_upload_callback)
410
411
files_to_upload = [
412
('file1.txt', 'First file'),
413
('file2.txt', 'Second file'),
414
('file3.txt', 'Third file')
415
]
416
417
for i, (filename, name) in enumerate(files_to_upload):
418
media = MediaFileUpload(filename, mimetype='text/plain')
419
file_metadata = {'name': name}
420
421
request = service.files().create(
422
body=file_metadata,
423
media_body=media
424
)
425
426
batch.add(request, request_id=f'upload_{i}')
427
428
# Execute all uploads
429
batch.execute()
430
```
431
432
### Upload with Retry Logic
433
434
```python
435
from googleapiclient.errors import HttpError, ResumableUploadError
436
import time
437
import random
438
439
def upload_with_retry(service, filename, file_metadata, max_retries=3):
440
"""Upload with exponential backoff retry logic."""
441
442
media = MediaFileUpload(filename, resumable=True)
443
request = service.files().create(body=file_metadata, media_body=media)
444
445
for attempt in range(max_retries):
446
try:
447
response = None
448
while response is None:
449
status, response = request.next_chunk()
450
if status:
451
print(f"Upload progress: {int(status.progress() * 100)}%")
452
return response
453
454
except (HttpError, ResumableUploadError) as error:
455
if attempt < max_retries - 1:
456
wait_time = (2 ** attempt) + (random.randint(0, 1000) / 1000)
457
print(f"Upload failed, retrying in {wait_time:.1f} seconds...")
458
time.sleep(wait_time)
459
continue
460
else:
461
raise
462
463
# Use retry upload
464
file_metadata = {'name': 'important-file.pdf'}
465
result = upload_with_retry(service, 'important-file.pdf', file_metadata)
466
print(f'Upload successful: {result.get("id")}')
467
```