The body-parser middleware commonly used for Express can parse only content-Type as

  • application/json
  • application/x-www-form-urlencoded
  • application/octet-stream
  • text/plain

Four types of data, other types (such as multipart/form-data for uploading images), are useless.

For image uploads, there is a specialized middleware called Multer that handles data with a content-Type of multipart/form-data.

Basic usage of multer

The installation

npm install --save multer
Copy the code

Epress

var express = require('express') var multer = require('multer') var upload = multer({ dest: 'uploads/') var app = express() Avatar app.post('/profile', upload.single('avatar'), function (req, res, next) {// req. // req.body will have text field data, if present}) // Upload 12 images, Post ('/photos/upload', upload.array('photos', 12), function (req, res, Next) {// req.files array type, containing multiple files // req.body will have text field data, if any}) var cpUpload = upload.fields([{name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8}]) app.post('/cool-profile', cpUpload, function (req, res, next) {// req.files is an object (String -> Array) key is a file name, Files ['avatar'][0] -> File // req.files['gallery'] -> Array // // req.body will have text field data, if it exists})Copy the code

If you need to process a form with only a text field and no file submission, you should use.none():

var express = require('express') var app = express() var multer = require('multer') var upload = multer() App.post ('/profile', upload.none(), function (req, res, next) {// req.body contains text fields})Copy the code

The file contains the following information

Key Description Note
fieldname Field name is specified by the form
originalname The name of the file on the user’s computer
encoding File coding
mimetype The MIME type of the file
size File size in bytes
destination Save the path DiskStorage
filename Stored in thedestinationFile name in DiskStorage
path The full path of the uploaded file DiskStorage
buffer One that holds the entire fileBuffer MemoryStorage

multer(opts)

The Multer accepts an options object, the most basic of which is the dest attribute, which tells the Multer where to save the uploaded file. If you omit the Options object, the files are kept in memory and never written to disk. Avoid memory overflow when writing data to the memory and try to write data to the disk.

To avoid naming conflicts, Multer changes the file name of the uploaded file. This renaming function can be customized to your needs.

Here are the options you can pass to Multer.

Key Description
dest or storage Where do I store files
fileFilter File filters that control which files can be accepted
limits Limit uploaded data
preservePath Save the full file path with the file name

In general, for a normal web application, you just need to set the dest attribute like this:

var upload = multer({ dest: 'uploads/' })
Copy the code

If you want more control over uploading, you can use the storage option instead of Dest. Multer has DiskStorage and MemoryStorage engines. There are also more available from third parties.

.single(fieldname)

Accept a file named FieldName. The information for this file is saved in req.file.

.array(fieldname[, maxCount])

Accepts an array of files named FieldName. MaxCount can be configured to limit the maximum number of uploads. Information about these files is saved in req.files.

.fields(fields)

Accepts mixed files for the specified fields. Information about these files is saved in req.files.

Fields should be an array of objects and should have a name and optional maxCount attribute.

Example:

[
  { name: 'avatar', maxCount: 1 },
  { name: 'gallery', maxCount: 8 }
]
Copy the code

.none()

Only text fields are accepted. “LIMIT_UNEXPECTED_FILE” error occurs if any files are uploaded to this mode. This has the same effect as upload.fields([]).

.any()

Accept all uploaded files. The array of files will be saved in req.files.

Warning: Make sure you always handle user file uploads. Never use multer as global middleware, because malicious users can upload files to a route you didn’t expect, and should only be used on routes where you need to handle uploading files.

storage

Disk storage engine (DiskStorage)

The disk storage engine allows you to control the storage of files.

var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, '/tmp/my-uploads')
  },
  filename: function (req, file, cb) {
    cb(null, file.fieldname + '-' + Date.now())
  }
})

var upload = multer({ storage: storage })
Copy the code

There are two options available, Destination and filename. They are all functions used to determine where files are stored.

Destination is used to determine in which folder the uploaded file should be stored. You can also provide a string (e.g. ‘/ TMP /uploads’). If destination is not set, the default temporary folder of the operating system is used.

Note: If you provide destination as a function, you are responsible for creating the folder. When providing a string, multer will ensure that the folder is created by you.

Filename is used to determine the name of the file in the folder. If filename is not set, each file is set to a random filename with no extension.

Note: Multer does not add any extensions for you, and your program should return a full filename.

Each function passes the request object (REQ) and some information about the file (file) to help with your decision.

Note that the req.body may not be fully populated, depending on the order in which fields and files are sent to the server to the client.

Memory storage engine (MemoryStorage)

The memory storage engine stores files in Buffer objects in memory without any options.

var storage = multer.memoryStorage()
var upload = multer({ storage: storage })
Copy the code

When using an in-memory storage engine, the file information will contain a buffer field that contains the entire file data.

Warning: When you use memory storage, uploading very large files, or too many small files, can cause your application to run out of memory.

limits

An object that specifies some data size limits. Multer uses busboy through this object. Detailed features can be found on Busboy’s page.

You can use the following:

Key Description Default
fieldNameSize Field Indicates the maximum length of the name 100 bytes
fieldSize The maximum length of the field value 1MB
fields Maximum number of non-file fields infinite
fileSize Maximum file length in bytes in a multipart form infinite
files In the multipart form, the maximum number of files infinite
parts In the multipart form, the maximum number of parts to transfer (fields + files) infinite
headerPairs The maximum number of key-value pairs in a multipart form 2000

Limits can help protect your site from denial of service (DoS) attacks.

fileFilter

Set up a function to control what files can be uploaded and what files should be skipped. The function should look like this:

Function fileFilter (req, file, cb) {// This function should call 'cb' using Boolean to indicate whether the file should be accepted // reject the file using 'false', like this: Cb (null, false) // Accept the file and use 'true', like this: cb(null, true) // If there is a problem, you can always send an Error like this: cb(new Error('I don't have a clue! '))}Copy the code

Error handling

When an error is encountered, Multer will send the error to Express. You can use a better error display page (express standard).

If you want to catch errors from multer, you can call the middleware program yourself. If you want to catch Multer errors, you can use the MulterError class under the Multer object (err Instanceof multer.multerError).

var multer = require('multer') var upload = multer().single('avatar') app.post('/profile', function (req, res) { upload(req, res, Function (err) {if (err instanceof multer.multerError) {if (err instanceof multer.multerError) {else if (err) {err instanceof multer.multerError)})})Copy the code

The front-end code

Direct form submission

<form action="/profile" method="post" encType ="multipart/form-data"> <h2> <input type="file" name="avatar"> < form> </form>Copy the code

Form submission requires encType =”multipart/form-data” to ensure that the content-Type is multipart/form-data

JQuery asynchronous submission

<input type="file" name="avatar" id="fileUploader" SRC = "http://cdn.bootcss.com/jquery/3.1.0/jquery.min.js" > < / script > < script > function uploadFile () {var formData = new FormData(); var file = document.getElementById('fileUploader').files[0]; Formdata. append('avatar', file); $.ajax({ url : '/profile', type : 'post', data : FormData, // processData Defaults to true, will convert data to string transmission, must be set to false processData: False, // contentType default 'application/x-www-form-urlencoded; Charset = utf-8 '// Do not set multipart/form-data to false, contentType: false, success: function (res) { console.log(res); }, }) } </script>Copy the code

multipart/form-data

A common way to submit data. When we use forms to upload files, we must let

Request Headers
POST /profile HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 20502
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:3000
X-Requested-With: XMLHttpRequest
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryIRoGm8wWREx5foPw

Form Data
------WebKitFormBoundaryIRoGm8wWREx5foPw
Content-Disposition: form-data; name="avatar"; filename="2646377831-58e393a0c7822_articlex.png"
Content-Type: image/png


------WebKitFormBoundaryIRoGm8wWREx5foPw--
Copy the code

First, a boundary was generated to divide different fields. In order to avoid repetition with the text, the boundary was very long and complicated.

Then the content-Type indicates that the data is encoded with multipart/form-data, and what is the Content of boundary in this request.

The message body is divided into several parts with similar structure according to the number of fields. Each part starts with –boundary, followed by content description information, carriage return, and finally the specific content of the field (text or binary). If you are transferring a file, also include the filename and file type information.

The message body ends with a –boundary– identifier.

Here’s an example:

<form action="/profile" method="post" enctype="multipart/form-data"> <input type="file" name="avatar" > <input Check</input> <button type="submit" </button>  </form>Copy the code

Click Submit and submit as follows

POST /profile HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 1814270
Pragma: no-cache
Cache-Control: no-cache
Origin: http://localhost:3000
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryJuLO36ZzIGEt5gGs

------WebKitFormBoundaryJuLO36ZzIGEt5gGs
Content-Disposition: form-data; name="avatar"; filename="red-btn.png"
Content-Type: image/png

(imagedata)
------WebKitFormBoundaryJuLO36ZzIGEt5gGs
Content-Disposition: form-data; name="myTextField"

2
------WebKitFormBoundaryJuLO36ZzIGEt5gGs
Content-Disposition: form-data; name="myCheckBox"

on
------WebKitFormBoundaryJuLO36ZzIGEt5gGs--
Copy the code

Reference Documents:

The jQuery ajax document: api.jquery.com/jquery.ajax…

Multer documentation: github.com/expressjs/m…