Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion storage/disk.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ function getDestination (req, file, cb) {

function DiskStorage (opts) {
this.getFilename = (opts.filename || getFilename)
// When `flush` is truthy, the disk storage asks the underlying write
// stream to fdatasync() the uploaded file before `_handleFile` reports
// success, so callers that expect the data to survive a crash or power
// loss (see #1381) can rely on it. The option is forwarded as
// `fs.createWriteStream(..., { flush: true })`, which was added in
// Node.js 20.10 / 21.0. On older runtimes the option is ignored and
// behavior falls back to the previous buffered-write semantics.
this.flush = opts.flush === true

if (typeof opts.destination === 'string') {
fs.mkdirSync(opts.destination, { recursive: true })
Expand All @@ -34,7 +42,8 @@ DiskStorage.prototype._handleFile = function _handleFile (req, file, cb) {
if (err) return cb(err)

var finalPath = path.join(destination, filename)
var outStream = fs.createWriteStream(finalPath)
var writeStreamOptions = that.flush ? { flush: true } : undefined
var outStream = fs.createWriteStream(finalPath, writeStreamOptions)

file.stream.pipe(outStream)
outStream.on('error', cb)
Expand Down
55 changes: 55 additions & 0 deletions test/disk-storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,59 @@ describe('Disk Storage', function () {
done()
})
})

it('should support the flush option for durable uploads (#1381)', function (done) {
var calls = []
var realCreateWriteStream = fs.createWriteStream
fs.createWriteStream = function (filePath, options) {
calls.push({ filePath: filePath, options: options })
return realCreateWriteStream.apply(fs, arguments)
}

var storage = multer.diskStorage({ destination: uploadDir, flush: true })
var flushUpload = multer({ storage: storage })
var parser = flushUpload.single('small0')

var form = new FormData()
form.append('small0', util.file('small0.dat'))

util.submitForm(parser, form, function (err, req) {
fs.createWriteStream = realCreateWriteStream
assert.ifError(err)

assert.strictEqual(calls.length, 1)
assert.ok(calls[0].options && calls[0].options.flush === true,
'expected createWriteStream to be called with { flush: true }')

assert.strictEqual(req.file.fieldname, 'small0')
assert.strictEqual(req.file.size, 1778)
assert.strictEqual(util.fileSize(req.file.path), 1778)

done()
})
})

it('should default to buffered writes when flush is not set', function (done) {
var calls = []
var realCreateWriteStream = fs.createWriteStream
fs.createWriteStream = function (filePath, options) {
calls.push({ filePath: filePath, options: options })
return realCreateWriteStream.apply(fs, arguments)
}

var parser = upload.single('small0')
var form = new FormData()
form.append('small0', util.file('small0.dat'))

util.submitForm(parser, form, function (err, req) {
fs.createWriteStream = realCreateWriteStream
assert.ifError(err)

assert.strictEqual(calls.length, 1)
assert.strictEqual(calls[0].options, undefined,
'expected createWriteStream to be called with no options when flush is not set')

done()
})
})
})