/* eslint-disable @typescript-eslint/no-unused-expressions */ import fastify from 'fastify' import fastifyMultipart, { MultipartValue, MultipartFields, MultipartFile } from '.' import * as util from 'node:util' import { pipeline } from 'node:stream' import * as fs from 'node:fs' import { expect } from 'tstyche' import { FastifyErrorConstructor } from '@fastify/error' import { BusboyConfig, BusboyFileStream } from '@fastify/busboy' const pump = util.promisify(pipeline) const runServer = async () => { const app = fastify() app.register(fastifyMultipart, { preservePath: true, // field inherited from `BusboyConfig` interface attachFieldsToBody: true, limits: { parts: 500 }, onFile: (part: MultipartFile) => { console.log(part) } }) // usage app.post('/', async (req, reply) => { expect(req.formData()).type.toBe>() const data = await req.file() if (data == null) throw new Error('missing file') expect(data.type).type.toBe<'file'>() expect(data.file).type.toBe() expect(data.file.truncated).type.toBe() expect(data.fields).type.toBe() expect(data.fieldname).type.toBe() expect(data.filename).type.toBe() expect(data.encoding).type.toBe() expect(data.mimetype).type.toBe() const field = data.fields.myField if (field === undefined) { // field missing from the request } else if (Array.isArray(field)) { // multiple fields with the same name } else if (field.type === 'file') { // field containing a file field.file.resume() } else { // field containing a value field.fields.value } await pump(data.file, fs.createWriteStream(data.filename)) reply.send() }) // Multiple fields including scalar values app.post<{ Body: { file: MultipartFile, foo: MultipartValue } }>('/upload/stringvalue', async (req, reply) => { expect(req.body.foo).type.not.toHaveProperty('file') expect(req.body.foo.type).type.toBe<'field'>() expect(req.body.foo.value).type.toBe() expect(req.body.file.file).type.toBe() expect(req.body.file.type).type.toBe<'file'>() reply.send() }) app.post<{ Body: { file: MultipartFile, num: MultipartValue } }>('/upload/stringvalue', async (req, reply) => { expect(req.body.num.value).type.toBe() reply.send() expect(req.body.file.file).type.toBe() expect(req.body.file).type.not.toHaveProperty('value') }) // busboy app.post('/', async function (req, reply) { const data = await req.file({ limits: { fileSize: 1000, parts: 500 }, throwFileSizeLimit: true, sharedSchemaId: 'schemaId', isPartAFile: (fieldName, contentType, fileName) => { expect(fieldName).type.toBe() expect(contentType).type.toBe() expect(fileName).type.toBe() return true } }) if (!data) throw new Error('missing file') await pump(data.file, fs.createWriteStream(data.filename)) reply.send() }) // handle multiple file streams app.post('/', async (req, reply) => { const parts = req.files({ limits: { fileSize: 1000, parts: 500 }, throwFileSizeLimit: true, sharedSchemaId: 'schemaId', isPartAFile: (fieldName, contentType, fileName) => { expect(fieldName).type.toBe() expect(contentType).type.toBe() expect(fileName).type.toBe() return true } }) for await (const part of parts) { await pump(part.file, fs.createWriteStream(part.filename)) } reply.send() }) // handle multiple file streams and fields app.post('/upload/raw/any', async function (req, reply) { const parts = req.parts() for await (const part of parts) { if (part.type === 'file') { await pump(part.file, fs.createWriteStream(part.filename)) } else { console.log(part.value) } } reply.send() }) // accumulate whole file in memory app.post('/upload/raw/any', async function (req, reply) { const data = await req.file() if (!data) throw new Error('missing file') expect(await data.toBuffer()).type.toBe() // upload to S3 reply.send() }) // upload files to disk and work with temporary file paths app.post('/upload/files', async function (req, reply) { // stores files to tmp dir and return files + values const { files, values } = await req.saveRequestFiles() files[0]!.type // "file" files[0]!.filepath files[0]!.fieldname files[0]!.filename files[0]!.encoding files[0]!.mimetype files[0]!.fields // other parsed parts values.foo reply.send() }) // upload files to disk with busboy options app.post('/upload/files', async function (req, reply) { const options: Partial = { limits: { fileSize: 1000 } } await req.saveRequestFiles(options) reply.send() }) // access all errors app.post('/upload/files', async function (_req, reply) { const { FilesLimitError } = app.multipartErrors expect(app.multipartErrors.FieldsLimitError).type.toBe() expect(app.multipartErrors.FilesLimitError).type.toBe() expect(app.multipartErrors.InvalidMultipartContentTypeError).type.toBe() expect(app.multipartErrors.PartsLimitError).type.toBe() expect(app.multipartErrors.PrototypeViolationError).type.toBe() expect(app.multipartErrors.RequestFileTooLargeError).type.toBe() // test instanceof Error const a = new FilesLimitError() if (a instanceof FilesLimitError) { console.log('FilesLimitError occurred.') } reply.send() }) app.post('/upload/files', { config: { multipartOptions: {} } }, async function (req, reply) { expect(req.routeOptions.config.multipartOptions).type.toBe>() reply.send() }) app.post('/upload/files', async function (req, reply) { expect(req.routeOptions.config.multipartOptions).type.toBe | undefined>() reply.send() }) await app.ready() } runServer().then( console.log.bind(console, 'Success'), console.error.bind(console, 'Error') )