or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-middleware.mderror-handling.mdindex.mdstorage-engines.md

storage-engines.mddocs/

0

# Storage Engines

1

2

Storage engines determine where and how uploaded files are stored. Multer provides built-in disk and memory storage engines, with support for custom storage implementations.

3

4

## Capabilities

5

6

### Disk Storage

7

8

Stores uploaded files on the local file system with configurable destination and filename handling.

9

10

```javascript { .api }

11

/**

12

* Creates a disk storage engine for saving files to the file system

13

* @param options - Configuration options for disk storage

14

* @returns DiskStorage instance

15

*/

16

const diskStorage = multer.diskStorage(options: DiskStorageOptions);

17

18

interface DiskStorageOptions {

19

/** Directory or function to determine where files are stored */

20

destination?: string | DestinationFunction;

21

/** Function to determine the filename for stored files */

22

filename?: FilenameFunction;

23

}

24

25

type DestinationFunction = (

26

req: Request,

27

file: File,

28

cb: (error: Error | null, destination: string) => void

29

) => void;

30

31

type FilenameFunction = (

32

req: Request,

33

file: File,

34

cb: (error: Error | null, filename: string) => void

35

) => void;

36

```

37

38

**Usage Examples:**

39

40

```javascript

41

const multer = require('multer');

42

const path = require('path');

43

44

// Simple disk storage with static destination

45

const storage = multer.diskStorage({

46

destination: './uploads',

47

filename: (req, file, cb) => {

48

// Generate unique filename with timestamp

49

const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);

50

cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));

51

}

52

});

53

54

// Dynamic destination based on file type

55

const dynamicStorage = multer.diskStorage({

56

destination: (req, file, cb) => {

57

let folder = 'uploads/';

58

if (file.mimetype.startsWith('image/')) {

59

folder += 'images/';

60

} else if (file.mimetype === 'application/pdf') {

61

folder += 'documents/';

62

} else {

63

folder += 'others/';

64

}

65

cb(null, folder);

66

},

67

filename: (req, file, cb) => {

68

// Keep original filename with timestamp prefix

69

cb(null, Date.now() + '-' + file.originalname);

70

}

71

});

72

73

// User-specific uploads

74

const userStorage = multer.diskStorage({

75

destination: (req, file, cb) => {

76

const userId = req.user ? req.user.id : 'anonymous';

77

cb(null, `uploads/users/${userId}/`);

78

},

79

filename: (req, file, cb) => {

80

// Sanitize filename and add timestamp

81

const sanitized = file.originalname.replace(/[^a-zA-Z0-9.-]/g, '_');

82

cb(null, Date.now() + '-' + sanitized);

83

}

84

});

85

86

const upload = multer({ storage: storage });

87

```

88

89

### Memory Storage

90

91

Stores uploaded files in memory as Buffer objects, useful for temporary processing or cloud storage uploads.

92

93

```javascript { .api }

94

/**

95

* Creates a memory storage engine for keeping files in memory

96

* @returns MemoryStorage instance

97

*/

98

const memoryStorage = multer.memoryStorage();

99

```

100

101

**Usage Examples:**

102

103

```javascript

104

const multer = require('multer');

105

const storage = multer.memoryStorage();

106

const upload = multer({ storage: storage });

107

108

app.post('/process-image', upload.single('image'), (req, res) => {

109

if (req.file) {

110

// File is available as Buffer in req.file.buffer

111

console.log('File size:', req.file.buffer.length);

112

console.log('File type:', req.file.mimetype);

113

114

// Process the buffer (resize, compress, etc.)

115

processImageBuffer(req.file.buffer)

116

.then(processedBuffer => {

117

// Upload to cloud storage, save to database, etc.

118

return uploadToCloud(processedBuffer, req.file.originalname);

119

})

120

.then(cloudUrl => {

121

res.json({ success: true, url: cloudUrl });

122

})

123

.catch(error => {

124

res.status(500).json({ error: error.message });

125

});

126

} else {

127

res.status(400).json({ error: 'No file uploaded' });

128

}

129

});

130

131

// Batch processing multiple files in memory

132

app.post('/batch-process', upload.array('files', 10), (req, res) => {

133

if (req.files && req.files.length > 0) {

134

const processPromises = req.files.map(file => {

135

return processFileBuffer(file.buffer, file.mimetype);

136

});

137

138

Promise.all(processPromises)

139

.then(results => {

140

res.json({ processed: results.length, results });

141

})

142

.catch(error => {

143

res.status(500).json({ error: error.message });

144

});

145

}

146

});

147

```

148

149

### File Objects by Storage Type

150

151

Different storage engines add different properties to the file object:

152

153

```javascript { .api }

154

// DiskStorage file object

155

interface DiskStorageFile extends File {

156

/** Directory where the file was saved */

157

destination: string;

158

/** Name of the file within the destination directory */

159

filename: string;

160

/** Full path to the saved file */

161

path: string;

162

/** Size of the file in bytes */

163

size: number;

164

}

165

166

// MemoryStorage file object

167

interface MemoryStorageFile extends File {

168

/** Buffer containing the entire file */

169

buffer: Buffer;

170

/** Size of the file in bytes */

171

size: number;

172

}

173

174

// Base file properties (available in both storage types)

175

interface File {

176

/** Field name from the form */

177

fieldname: string;

178

/** Original filename from user's computer */

179

originalname: string;

180

/** File encoding type */

181

encoding: string;

182

/** MIME type of the file */

183

mimetype: string;

184

}

185

```

186

187

### Custom Storage Engines

188

189

You can create custom storage engines by implementing the required interface:

190

191

```javascript { .api }

192

/**

193

* Custom storage engine interface

194

* Must implement _handleFile and _removeFile methods

195

*/

196

interface StorageEngine {

197

_handleFile(req: Request, file: File, cb: StorageCallback): void;

198

_removeFile(req: Request, file: File, cb: RemoveCallback): void;

199

}

200

201

type StorageCallback = (error: Error | null, info?: any) => void;

202

type RemoveCallback = (error: Error | null) => void;

203

```

204

205

**Custom Storage Example:**

206

207

```javascript

208

// Example: Custom storage that saves to a database

209

function DatabaseStorage(options) {

210

this.database = options.database;

211

}

212

213

DatabaseStorage.prototype._handleFile = function(req, file, cb) {

214

const chunks = [];

215

216

file.stream.on('data', chunk => chunks.push(chunk));

217

file.stream.on('error', cb);

218

file.stream.on('end', () => {

219

const buffer = Buffer.concat(chunks);

220

221

// Save to database

222

this.database.saveFile({

223

filename: file.originalname,

224

mimetype: file.mimetype,

225

data: buffer,

226

size: buffer.length

227

})

228

.then(result => {

229

cb(null, {

230

fileId: result.id,

231

size: buffer.length,

232

filename: file.originalname

233

});

234

})

235

.catch(cb);

236

});

237

};

238

239

DatabaseStorage.prototype._removeFile = function(req, file, cb) {

240

this.database.deleteFile(file.fileId)

241

.then(() => cb(null))

242

.catch(cb);

243

};

244

245

// Usage

246

const databaseStorage = new DatabaseStorage({ database: myDb });

247

const upload = multer({ storage: databaseStorage });

248

```

249

250

## Storage Configuration Best Practices

251

252

### Security Considerations

253

254

```javascript

255

// Secure filename generation

256

const secureFilename = (req, file, cb) => {

257

// Remove potentially dangerous characters

258

const sanitized = file.originalname.replace(/[^a-zA-Z0-9.-]/g, '_');

259

260

// Add timestamp to prevent conflicts

261

const timestamp = Date.now();

262

263

// Limit filename length

264

const maxLength = 100;

265

const truncated = sanitized.length > maxLength

266

? sanitized.substring(0, maxLength)

267

: sanitized;

268

269

cb(null, `${timestamp}-${truncated}`);

270

};

271

272

// Secure destination handling

273

const secureDestination = (req, file, cb) => {

274

// Prevent directory traversal

275

const userId = req.user?.id?.replace(/[^a-zA-Z0-9]/g, '') || 'anonymous';

276

const safeDestination = path.join('uploads', userId);

277

278

// Ensure directory exists

279

fs.makedirs(safeDestination, { recursive: true }, (err) => {

280

if (err) return cb(err);

281

cb(null, safeDestination);

282

});

283

};

284

```

285

286

### Performance Considerations

287

288

```javascript

289

// For high-volume uploads, use disk storage

290

const highVolumeStorage = multer.diskStorage({

291

destination: './uploads',

292

filename: (req, file, cb) => {

293

// Use crypto for truly unique filenames

294

const crypto = require('crypto');

295

const hash = crypto.randomBytes(16).toString('hex');

296

cb(null, hash + path.extname(file.originalname));

297

}

298

});

299

300

// For temporary processing, use memory storage with limits

301

const tempProcessingUpload = multer({

302

storage: multer.memoryStorage(),

303

limits: {

304

fileSize: 10 * 1024 * 1024, // 10MB limit

305

files: 1 // Single file only

306

}

307

});

308

```