From 8c15d5bc54bc350efbe6684fc23c4d7d30680d1f Mon Sep 17 00:00:00 2001 From: KaKa Date: Thu, 19 Aug 2021 16:23:24 +0800 Subject: [PATCH] feat: auto create upload dir (#16) * feat: auto create upload dir * chore: explain more on symbols --- README.md | 15 +++++++++++++-- lib/index.ts | 38 ++++++++++++++++++++++++++++---------- test/uploadDir.test.ts | 25 +++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 test/uploadDir.test.ts diff --git a/README.md b/README.md index 6a79a1d..31582f1 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ yarn add fastify-formidable ## Usage ```ts -import FastifyFormidable from 'fastify-formidable' +import FastifyFormidable, { kFileSavedPaths, kFileSavedPaths, kIsMultipartParsed } from 'fastify-formidable' fastify.register(FastifyFormidable) @@ -35,6 +35,15 @@ fastify.post('/', async function(request, reply) { // access body // note that file fields will exist in body and it will becomes the file path saved on disk request.body + + // access all the files path + request[kFileSavedPaths] + + // check if it is multipart + if( request[kIsMultipart] === true ) {} + + // check if it is already parsed + if ( request[kIsMultipartParsed] === true ) {} }) // add content type parser which will automatic parse all `multipart/form-data` found @@ -59,7 +68,9 @@ import FastifyFormidable from 'fastify-formidable' fastify.register(FastifyFormidable, { formidable: { - maxFileSize: 1000 + maxFileSize: 1000, + // this folder will be automatic created by this plugin + uploadDir: '/' } }) ``` diff --git a/lib/index.ts b/lib/index.ts index 882afdc..6106ec0 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -3,14 +3,19 @@ import { FastifyPluginAsync, FastifyRequest } from 'fastify' import FastifyPlugin from 'fastify-plugin' import { Fields, File, Files, IncomingForm, Options } from 'formidable' import Formidable from 'formidable/Formidable' +import * as fs from 'fs' import { IncomingMessage } from 'http' -const kIsMultipart = Symbol.for('[isMultipart]') +export const kIsMultipart = Symbol.for('[FastifyMultipart.isMultipart]') +export const kIsMultipartParsed = Symbol.for('[FastifyMultipart.isMultipartParsed]') +export const kFileSavedPaths = Symbol.for('[FastifyMultipart.fileSavedPaths]') declare module 'fastify' { interface FastifyRequest { files: Files | null parseMultipart: (this: FastifyRequest, options?: Options) => Promise [kIsMultipart]: boolean + [kIsMultipartParsed]: boolean + [kFileSavedPaths]: string[] } } @@ -35,27 +40,40 @@ function promisify (func: Function): (request: IncomingMessage) => Promise<{ fie function buildRequestParser (formidable: Formidable): (request: FastifyRequest, options?: Pick) => Promise<{ body: Fields, files: Files }> { const parse = promisify(formidable.parse.bind(formidable)) return async function (request: FastifyRequest, options?: Pick): Promise<{ body: Fields, files: Files }> { + if (request[kIsMultipartParsed]) { + request.log.warn('multipart already parsed, you probably need to check your code why it is parsed twice.') + return { body: request.body as Fields, files: request.files as Files } + } const { fields, files } = await parse(request.raw) + request[kFileSavedPaths] = [] const body = Object.assign({}, fields) - if (options?.removeFilesFromBody !== true) { - Object.keys(files).forEach(function (key) { - (body as any)[key] = Array.isArray(files[key]) - ? (files[key] as File[]).map(function (file) { - return file.path - }) - : (files[key] as File).path - }) - } + Object.keys(files).forEach(function (key) { + const paths = Array.isArray(files[key]) + ? (files[key] as File[]).map(function (file) { + return file.path + }) + : (files[key] as File).path + if (options?.removeFilesFromBody !== true) (body as any)[key] = paths + request[kFileSavedPaths].push(...paths) + }) + request[kIsMultipartParsed] = true return { body, files } } } const plugin: FastifyPluginAsync = async function (fastify, options) { + // create upload folder when not exist + if (typeof options.formidable?.uploadDir === 'string') { + await fs.promises.mkdir(options.formidable.uploadDir, { recursive: true }) + } + const formidable = new IncomingForm(options.formidable) fastify.decorateRequest(kIsMultipart, false) + fastify.decorateRequest(kIsMultipartParsed, false) + fastify.decorateRequest(kFileSavedPaths, null) fastify.decorateRequest('files', null) fastify.decorateRequest('parseMultipart', async function (this: FastifyRequest, decoratorOptions?: Options) { diff --git a/test/uploadDir.test.ts b/test/uploadDir.test.ts new file mode 100644 index 0000000..64e0b55 --- /dev/null +++ b/test/uploadDir.test.ts @@ -0,0 +1,25 @@ +import Fastify from 'fastify' +import * as fs from 'fs' +import * as path from 'path' +import FastifyFormidable from '../lib' + +const uploadDir = path.resolve('tmp') + +describe('uploadDir', function () { + test('create', async function () { + const fastify = Fastify() + await fastify.register(FastifyFormidable, { + formidable: { + uploadDir + } + }) + + const stat = await fs.promises.lstat(uploadDir) + expect(stat.isDirectory()).toStrictEqual(true) + }) + + afterEach(async function () { + // clean up + await fs.promises.rmdir(uploadDir) + }) +})