or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-management.mdexception-handling.mdindex.mdrequest-handling.mdstorage-resumability.mdupload-operations.md

storage-resumability.mddocs/

0

# Storage and Resumability

1

2

URL storage interfaces and implementations for resuming uploads across sessions. TusPy provides a pluggable storage system that allows uploads to be resumed even after application restarts.

3

4

## Capabilities

5

6

### Storage Interface

7

8

Abstract base class defining the storage API for URL persistence.

9

10

```python { .api }

11

from abc import ABC, abstractmethod

12

13

class Storage(ABC):

14

"""Abstract base class for URL storage implementations."""

15

16

@abstractmethod

17

def get_item(self, key: str) -> Optional[str]:

18

"""

19

Return the tus url of a file, identified by the key specified.

20

21

Parameters:

22

- key (str): The unique id for the stored item (upload URL)

23

24

Returns:

25

Optional[str]: The stored URL or None if not found

26

"""

27

28

@abstractmethod

29

def set_item(self, key: str, value: str):

30

"""

31

Store the url value under the unique key.

32

33

Parameters:

34

- key (str): The unique id to store the URL under

35

- value (str): The actual URL value to be stored

36

"""

37

38

@abstractmethod

39

def remove_item(self, key: str):

40

"""

41

Remove/Delete the url value under the unique key from storage.

42

43

Parameters:

44

- key (str): The unique id of the item to remove

45

"""

46

```

47

48

### File Storage Implementation

49

50

File-based storage implementation using TinyDB for URL persistence.

51

52

```python { .api }

53

class FileStorage(Storage):

54

"""File-based implementation of Storage interface using TinyDB."""

55

56

def __init__(self, fp: str):

57

"""

58

Initialize FileStorage with database file path.

59

60

Parameters:

61

- fp (str): File path for the TinyDB database

62

"""

63

64

def get_item(self, key: str) -> Optional[str]:

65

"""

66

Return the tus url of a file, identified by the key specified.

67

68

Parameters:

69

- key (str): The unique id for the stored item

70

71

Returns:

72

Optional[str]: The stored URL or None if not found

73

"""

74

75

def set_item(self, key: str, url: str):

76

"""

77

Store the url value under the unique key.

78

79

Updates existing entry if key already exists, otherwise creates new entry.

80

81

Parameters:

82

- key (str): The unique id to store the URL under

83

- url (str): The actual URL value to be stored

84

"""

85

86

def remove_item(self, key: str):

87

"""

88

Remove/Delete the url value under the unique key from storage.

89

90

Parameters:

91

- key (str): The unique id of the item to remove

92

"""

93

94

def close(self):

95

"""

96

Close the file storage and release all opened files.

97

98

Should be called when done with storage to ensure proper cleanup.

99

"""

100

```

101

102

### Fingerprint Interface

103

104

Abstract base class for generating unique file fingerprints used as storage keys.

105

106

```python { .api }

107

from abc import ABC, abstractmethod

108

from typing import IO

109

110

class Fingerprint(ABC):

111

"""An interface specifying the requirements of a file fingerprint."""

112

113

@abstractmethod

114

def get_fingerprint(self, fs: IO) -> str:

115

"""

116

Return a unique fingerprint string value based on the file stream received.

117

118

Parameters:

119

- fs (IO): The file stream instance to generate fingerprint for

120

121

Returns:

122

str: Unique fingerprint string identifying the file

123

"""

124

```

125

126

### MD5 Fingerprint Implementation

127

128

MD5-based fingerprint implementation using file content and size.

129

130

```python { .api }

131

class Fingerprint(Fingerprint):

132

"""MD5-based fingerprint implementation using file content and size."""

133

134

BLOCK_SIZE = 65536 # Block size for reading file content

135

136

def get_fingerprint(self, fs: IO) -> str:

137

"""

138

Return a unique fingerprint string based on MD5 hash and file size.

139

140

Generates fingerprint using MD5 hash of first block and file size

141

to minimize collision chances while being efficient.

142

143

Parameters:

144

- fs (IO): The file stream instance to generate fingerprint for

145

146

Returns:

147

str: Fingerprint in format "size:{size}--md5:{hash}"

148

"""

149

```

150

151

## Usage Examples

152

153

### Basic Resumable Upload

154

155

```python

156

from tusclient import client

157

from tusclient.storage.filestorage import FileStorage

158

159

# Create file storage for URL persistence

160

storage = FileStorage('/tmp/tuspy_uploads.db')

161

162

# Create client and uploader with resumability enabled

163

my_client = client.TusClient('http://tusd.tusdemo.net/files/')

164

uploader = my_client.uploader(

165

'/path/to/large_file.ext',

166

chunk_size=5*1024*1024, # 5MB chunks

167

store_url=True, # Enable URL storage

168

url_storage=storage # Provide storage implementation

169

)

170

171

try:

172

# Upload will be resumable if interrupted

173

uploader.upload()

174

print("Upload completed successfully")

175

except KeyboardInterrupt:

176

print("Upload interrupted - can be resumed later")

177

finally:

178

storage.close()

179

```

180

181

### Resuming Interrupted Upload

182

183

```python

184

from tusclient import client

185

from tusclient.storage.filestorage import FileStorage

186

187

# Use same storage file as previous upload

188

storage = FileStorage('/tmp/tuspy_uploads.db')

189

190

# Create uploader for same file - will automatically resume

191

my_client = client.TusClient('http://tusd.tusdemo.net/files/')

192

uploader = my_client.uploader(

193

'/path/to/large_file.ext', # Same file path as before

194

chunk_size=5*1024*1024,

195

store_url=True,

196

url_storage=storage

197

)

198

199

# Check if resuming from previous upload

200

if uploader.offset > 0:

201

print(f"Resuming upload from offset {uploader.offset}")

202

progress = (uploader.offset / uploader.get_file_size()) * 100

203

print(f"Already uploaded: {progress:.1f}%")

204

205

try:

206

uploader.upload()

207

print("Upload completed")

208

finally:

209

storage.close()

210

```

211

212

### Custom Storage Implementation

213

214

```python

215

from tusclient.storage.interface import Storage

216

import redis

217

218

class RedisStorage(Storage):

219

"""Redis-based storage implementation."""

220

221

def __init__(self, redis_client):

222

self.redis = redis_client

223

224

def get_item(self, key: str) -> Optional[str]:

225

result = self.redis.get(f"tuspy:{key}")

226

return result.decode('utf-8') if result else None

227

228

def set_item(self, key: str, value: str):

229

self.redis.set(f"tuspy:{key}", value)

230

231

def remove_item(self, key: str):

232

self.redis.delete(f"tuspy:{key}")

233

234

# Use custom storage

235

redis_client = redis.Redis(host='localhost', port=6379, db=0)

236

storage = RedisStorage(redis_client)

237

238

my_client = client.TusClient('http://tusd.tusdemo.net/files/')

239

uploader = my_client.uploader(

240

'/path/to/file.ext',

241

store_url=True,

242

url_storage=storage

243

)

244

245

uploader.upload()

246

```

247

248

### Custom Fingerprint Implementation

249

250

```python

251

from tusclient.fingerprint.interface import Fingerprint

252

import hashlib

253

from typing import IO

254

255

class SHA256Fingerprint(Fingerprint):

256

"""SHA256-based fingerprint implementation."""

257

258

def get_fingerprint(self, fs: IO) -> str:

259

# Read entire small files, first 64KB of large files

260

fs.seek(0)

261

content = fs.read(65536)

262

263

# Get file size

264

fs.seek(0, 2) # Seek to end

265

size = fs.tell()

266

267

# Generate SHA256 hash

268

hasher = hashlib.sha256()

269

hasher.update(content if isinstance(content, bytes) else content.encode('utf-8'))

270

271

return f"size:{size}--sha256:{hasher.hexdigest()}"

272

273

# Use custom fingerprint

274

from tusclient.storage.filestorage import FileStorage

275

276

storage = FileStorage('/tmp/tuspy_uploads.db')

277

custom_fingerprint = SHA256Fingerprint()

278

279

my_client = client.TusClient('http://tusd.tusdemo.net/files/')

280

uploader = my_client.uploader(

281

'/path/to/file.ext',

282

store_url=True,

283

url_storage=storage,

284

fingerprinter=custom_fingerprint

285

)

286

287

uploader.upload()

288

storage.close()

289

```

290

291

### Managing Stored URLs

292

293

```python

294

from tusclient.storage.filestorage import FileStorage

295

from tusclient.fingerprint.fingerprint import Fingerprint

296

297

# Create storage and fingerprint instances

298

storage = FileStorage('/tmp/tuspy_uploads.db')

299

fingerprinter = Fingerprint()

300

301

# Generate fingerprint for a file

302

with open('/path/to/file.ext', 'rb') as fs:

303

key = fingerprinter.get_fingerprint(fs)

304

305

# Check if URL is stored

306

stored_url = storage.get_item(key)

307

if stored_url:

308

print(f"Found stored URL: {stored_url}")

309

else:

310

print("No stored URL found for this file")

311

312

# Manually remove stored URL (e.g., after successful upload)

313

storage.remove_item(key)

314

315

storage.close()

316

```