You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

136 lines
3.5 KiB

import { FastifyRequest, preHandlerHookHandler } from 'fastify'
import makePreHandler from './lib/make-prehandler'
import diskStorage from './storage/disk'
import memoryStorage from './storage/memory'
import MulterError from './lib/multer-error'
import contentParser from './lib/content-parser'
import {
Field,
File,
Options,
FileFilter,
FileFilterCallback,
Setup,
StorageEngine,
} from './interfaces'
import { Strategy } from './lib/file-appender'
function allowAll(_req: FastifyRequest, _file: File, cb: FileFilterCallback) {
cb(null, true)
}
class Multer {
storage: StorageEngine
limits: Options['limits']
preservePath: Options['preservePath']
fileFilter: FileFilter
contentParser: typeof contentParser
constructor(options: Options) {
if (options.storage) {
this.storage = options.storage
} else if (options.dest) {
this.storage = diskStorage({ destination: options.dest })
} else {
this.storage = memoryStorage()
}
this.limits = options.limits
this.preservePath = options.preservePath
this.fileFilter = options.fileFilter || allowAll
this.contentParser = contentParser
}
private _makePreHandler(fields: Field[], fileStrategy: Strategy) {
const setup: Setup = () => {
const fileFilter = this.fileFilter
const filesLeft = Object.create(null)
fields.forEach(function(field) {
if (typeof field.maxCount === 'number') {
filesLeft[field.name] = field.maxCount
} else {
filesLeft[field.name] = Infinity
}
})
function wrappedFileFilter(req: FastifyRequest, file: File, cb: FileFilterCallback) {
if ((filesLeft[file.fieldname] || 0) <= 0) {
return cb(new MulterError('LIMIT_UNEXPECTED_FILE', file.fieldname))
}
filesLeft[file.fieldname] -= 1
fileFilter(req, file, cb)
}
return {
limits: this.limits,
preservePath: this.preservePath,
storage: this.storage,
fileFilter: wrappedFileFilter,
fileStrategy,
}
}
return makePreHandler(setup)
}
single(name: string): preHandlerHookHandler {
return this._makePreHandler([{ name, maxCount: 1 }], 'VALUE')
}
array(name: string, maxCount?: number): preHandlerHookHandler {
return this._makePreHandler([{ name, maxCount }], 'ARRAY')
}
fields(fields: Field[]): preHandlerHookHandler {
return this._makePreHandler(fields, 'OBJECT')
}
none(): preHandlerHookHandler {
return this._makePreHandler([], 'NONE')
}
any(): preHandlerHookHandler {
const setup: Setup = () => ({
limits: this.limits,
preservePath: this.preservePath,
storage: this.storage,
fileFilter: this.fileFilter,
fileStrategy: 'ARRAY',
})
return makePreHandler(setup)
}
}
interface MulterFactory {
(options?: Options | undefined): Multer
contentParser: typeof contentParser
diskStorage: typeof diskStorage
memoryStorage: typeof memoryStorage
MulterError: typeof MulterError
default: MulterFactory
}
const multer: any = function(options?: Options) {
if (options === undefined) {
return new Multer({})
}
if (typeof options === 'object' && options !== null) {
return new Multer(options)
}
throw new TypeError('Expected object for argument options')
}
multer.contentParser = contentParser
multer.diskStorage = diskStorage
multer.memoryStorage = memoryStorage
multer.MulterError = MulterError
multer.default = multer
export = multer as MulterFactory