0.3.0 • Published 5 years ago

@medley/multipart v0.3.0

Weekly downloads
-
License
MIT
Repository
github
Last release
5 years ago

@medley/multipart

npm Version Build Status Coverage Status dependencies Status

A Medley plugin for parsing multipart/form-data request bodies, which are primarily used for uploading files. It is written on top of Busboy and inspired by multer.

Installation

# npm
npm install @medley/multipart

# yarn
yarn add @medley/multipart

Usage

Registering the plugin adds a .multipart() method to the app. This method creates a preHandler hook that will parse multipart/form-data bodies.

The hook will add a body and a files property to the req object if it completes successfully, where req.body will contain the text values of the form and req.files will contain the uploaded files.

If an error occurs, req.body and req.files will remain undefined.

Example:

<form action="/profile" method="post" enctype="multipart/form-data">
  <input type="text" name="firstName" />
  <input type="file" name="profilePhoto" />
</form>
const medley = require('@medley/medley');
const app = medley();

app.register(require('@medley/multipart'));

app.post('/profile', [
  app.multipart({
    profilePhoto: {maxCount: 1},
  }),
], (req, res, next) => {
  req.files.profilePhoto // The uploaded file (see File Object below)
  req.body.firstName // The user's first name
});

API

Plugin Options

OptionTypeDescriptionDefault
preservePathbooleanIf paths in the multipart 'filename' field shall be preserved.false
limitsobjectVarious limits on incoming data. Valid properties are:
limits.fieldNameSizeintegerMax field name size (in bytes).100
limits.fieldSizeintegerMax field value size (in bytes).1 MiB
limits.fieldsintegerMax number of non-file fields.Infinity
limits.fileSizeintegerThe max file size (in bytes).Infinity
limits.filesintegerThe max number of file fields.Infinity
limits.partsintegerThe max number of parts (fields + files).Infinity
limits.headerPairsintegerThe max number of header key-value pairs to parse.2000

Specifying the limits can help protect your server against denial of service (DoS) attacks.

const medley = require('@medley/medley');
const app = medley();

app.register(require('@medley/multipart'), {
  limits: {
    fieldSize: 100 * 1024, // 100 KiB
    fileSize: 5 * 1024 * 1024, // 5 MiB
    files: 4,
  },
});

app.multipart(expectedFiles[, options])

OptionTypeDescription
expectedFilesobject or 'ANY_FILES'Required. An object mapping file field names to the maximum number of files expected for the field. See details below.
optionsobjectThe same as the global plugin option. Will be merged with the global plugin options (while taking precedence over them).

The options parameter allows for route-specific control over upload limits.

app.multipart({
  profilePhoto: {maxCount: 1},
}, {
  limits: {
    fields: 1,
    fileSize: 8 * 1024 * 1024, // 8 MiB
  },
})

expectedFiles

Specifies the expected file fields and limits the number of files that can be received for those fields. If unexpected files are received, the upload will be cancelled with an error.

{
  fieldName: { maxCount: integer, optional?: boolean} | integer
}
app.multipart({
  someFile: {maxCount: 1},
  multipleFiles: {maxCount: 6},
  moreFiles: 5, // maxCount shorthand
})

Expected files are required by default, so an error will be thrown if an expected file is not uploaded. Set optional: true to allow the upload to complete without receiving any files for a particular field.

app.post('/upload', [
  app.multipart({
    requiredFile: {maxCount: 1},
    optionalFiles: {maxCount: 5, optional: true},
  }),
], (req, res, next) => {
  req.files.requiredFile // Will always exist
  req.files.optionalFiles // May be `undefined` or an array of files
});

The maxCount value determines the value type of the property in the req.files object. If maxCount is 1, the value will be a file object. If maxCount > 1, the value will be an array of file objects (even if only a single file is received).

app.post('/upload', [
  app.multipart({
    oneFile: {maxCount: 1},
    multipleFiles: {maxCount: 5},
  }),
], (req, res, next) => {
  req.files.oneFile // { ... }
  req.files.multipleFiles // [ { ... }, { ... }, { ... } ]
});

If expectedFiles is the special string 'ANY_FILES', any files may be uploaded and file objects will always be stored in an array. Note that the 'ANY_FILES' option should not be used in a production environment.

multipart.discardFiles(files)

Exported directly by the module, this method safely discards all of the files in the req.files object.

This is only needed when the multipart preHandler hook completes successfully but the files won't be handled (e.g. if an error happens).

const multipart = require('@medley/multipart');

// ...

// Something went wrong and the files need to be discarded
multipart.discardFiles(req.files);

multipart.MultipartError

The error constructor that this plugin uses to create errors when something goes wrong. It can be used to check if an error was generated by multipart inside an error handler.

const {MultipartError} = require('@medley/multipart');

app.setErrorHandler((err, req, res) => {
  if (err instanceof MultipartError) {
    // This is a multipart error
  }
});

See the code for the properties attached to MultipartError objects.

File Object

PropertyDescription
streamfs.ReadStream of the file.
fileNameThe name of the file on the user's computer. An empty string ('') if no name was supplied by the client.
mimeTypeThe MIME type of the file reported by the client using the Content-Type header.
sizeThe size of the file in bytes.

Note: The file stream must be handled in some way (even discarded by doing stream.destroy()) to ensure that the underlying file descriptor gets closed and the temporary file gets deleted.

app.post('/profile', [
  app.multipart({
    profilePhoto: {maxCount: 1},
  }),
], (req, res, next) => {
  const {profilePhoto} = req.files;

  profilePhoto.stream // ReadStream {}
  profilePhoto.fileName // 'me.jpg'
  profilePhoto.mimeType // 'image/jpeg'
  profilePhoto.size // 3483297
});

Error Handling

When encountering an error, multipart will delegate the error to Medley. Note that if multipart did not create the error, you may need to discard the uploaded files.

const multipart = require('@medley/multipart');

app.setErrorHandler((err, req, res) => {
  if (err instanceof multipart.MultipartError) {
    // This is a multipart error
  } else if (req.files !== undefined) {
    multipart.discardFiles(req.files);
  }
});
0.3.0

5 years ago

0.2.0

5 years ago

0.1.1

5 years ago

0.1.0

6 years ago