multer
Version:
Middleware for handling `multipart/form-data`.
98 lines (76 loc) • 2.87 kB
JavaScript
var fs = require('fs')
var os = require('os')
var path = require('path')
var crypto = require('crypto')
var pipeline = require('stream').pipeline
var MulterError = require('../lib/multer-error')
// Write streams still open for a file, so _removeFile can wait for the
// descriptor to be closed before unlinking (Windows refuses to unlink open files).
var openStreams = new WeakMap()
function getFilename (req, file, cb) {
crypto.randomBytes(16, function (err, raw) {
cb(err, err ? undefined : raw.toString('hex'))
})
}
function getDestination (req, file, cb) {
cb(null, os.tmpdir())
}
function DiskStorage (opts) {
this.getFilename = (opts.filename || getFilename)
if (typeof opts.destination === 'string') {
fs.mkdirSync(opts.destination, { recursive: true })
this.getDestination = function ($0, $1, cb) { cb(null, opts.destination) }
} else {
this.getDestination = (opts.destination || getDestination)
}
}
DiskStorage.prototype._handleFile = function _handleFile (req, file, cb) {
var that = this
that.getDestination(req, file, function (err, destination) {
if (err) return cb(err)
that.getFilename(req, file, function (err, filename) {
if (err) return cb(err)
var finalPath = path.join(destination, filename)
if (file.stream.destroyed) return cb(new MulterError('STREAM_DESTROYED'))
var outStream = fs.createWriteStream(finalPath)
file.path = finalPath
openStreams.set(file, outStream)
outStream.once('close', function () { openStreams.delete(file) })
pipeline(file.stream, outStream, function (err) {
if (err) return cb(err)
cb(null, {
destination: destination,
filename: filename,
path: finalPath,
size: outStream.bytesWritten
})
})
})
})
}
DiskStorage.prototype._removeFile = function _removeFile (req, file, cb) {
var path = file.path
delete file.destination
delete file.filename
delete file.path
var outStream = openStreams.get(file)
if (!outStream) return fs.unlink(path, cb)
// Unlink only once the descriptor has been released. `closed` is set when
// the descriptor is closed on every supported Node.js version, whereas
// 'close' is not emitted after a write stream is destroyed with an error on
// Node.js < 14 (emitClose defaults to false there), so wait for 'close' or
// 'error', whichever comes first. destroy() is a no-op if the stream is
// already being torn down.
if (outStream.closed) return fs.unlink(path, cb)
function onReleased () {
outStream.removeListener('close', onReleased)
outStream.removeListener('error', onReleased)
fs.unlink(path, cb)
}
outStream.once('close', onReleased)
outStream.once('error', onReleased)
outStream.destroy()
}
module.exports = function (opts) {
return new DiskStorage(opts)
}