In Web development, file upload is a very common and important function. This article will introduce how to use node JS handle file upload.
front end
Interface
code
Because the focus is on the service side, we don't elaborate too much on the front-end code here, but directly on the code!
<template> <el-upload class="upload-demo" action="/api/files/upload" :on-preview="handlePreview" :on-remove="handleRemove" :before-remove="beforeRemove" multiple :limit="3" :on-success="handleAvatarSuccess" :on-exceed="handleExceed" :file-list="fileList" > <el-button size="small" type="primary">Click upload</el-button> <div slot="tip" class="el-upload__tip">Upload only jpg,png,gif Documents, and no more than 200 kb </div> </el-upload> </template> <script> export default { data() { return { fileList: [ { name: 'food.jpeg', url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100' } ] } }, methods: { handleAvatarSuccess(res, file) { if (res.code === 10000) { this.$message.error(res.message) this.fileList.splice(this.fileList.length, 1) } else { this.$message.success('Upload successful') } }, handleRemove(file, fileList) { console.log(file, fileList) }, handlePreview(file) { console.log(file) }, handleExceed(files, fileList) { this.$message.warning(`Currently, 3 files are limited to be selected, and this time ${files.length} Files selected ${files.length + fileList.length} Files`) }, beforeRemove(file, fileList) { return this.$confirm(`OK to remove ${file.name}?`) } } } </script> <style lang="scss" scoped> .upload-demo { text-align: center; margin-top: 100px; } </style>
Let's focus on the logic of the server!!
Server
Start service
server.js
const express = require('express') const app = express() const bodyParser = require('body-parser') app.use(bodyParser.urlencoded({ extended: false })) const uploadControl = (req, res, next) => { res.send('ok') } app.use('/api/files/upload', uploadControl) app.listen(3000, () => { console.log('localhost:3000'); })
If you install nodemon, it's best. Execute nodemon server js
Next, we have to consider uploading files
file information
Each file has 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 | Document code | |
mimetype | MIME type of file | |
size | File size (in bytes) | |
destination | Save path | DiskStorage |
filename | File name saved in destination | DiskStorage |
path | The full path of the uploaded file | DiskStorage |
buffer | A Buffer that stores the entire file | MemoryStorage |
Where can I access the uploaded files? req.file, the following is the print result, which we can understand by referring to the above file information table
Multer middleware (theoretical knowledge)
Here we use Multer This is a node JS middleware, which is used to process form data of multipart / form data type, and is mainly used to upload files. It's written in busboy It's very efficient.
Note: Multer will not process any form data of non multipart / form data type.
npm install --save multer
Multer([options])
Multer accepts an options object, the most basic of which is the dest attribute, which will tell multer where to save the uploaded file. If you omit the options object, these files will be saved in memory and will never be written to disk.
To avoid naming conflicts, Multer modifies the uploaded file name. This rename function can be customized according to your needs.
Here are the options you can pass to Multer.
Key | Description |
---|---|
dest or storage | Where to store files |
fileFilter | File filter, which controls which files can be accepted |
limits | Limit uploaded data |
preservePath | Save the full file path containing the file name |
.single(fieldname)
Accept a file named fieldname. The information of 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. The information of these files is saved in req files.
.fields(fields)
Accepts the mixed file of the specified fields. The information of these files is saved in req files.
fields should be an array of objects with name and optional maxCount attributes.
Example:
[ { name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 } ]
.none()
Only text fields are accepted. If any file is uploaded to this mode, a "LIMIT_UNEXPECTED_FILE" error will occur. This is similar to upload Fields ([]) has the same effect.
.any()
Accept all uploaded files. Save array in req file files.
Encapsulate your own upload middleware according to Multer
dest
Create a file middlewares / upload js
const path = require('path') const multer = require('multer') // Save the uploaded files in the public/uploads directory. Here is only the upload of demonstration files, so there is no database. Forgive me  ̄ □  ̄ | const upload = multer({ dest: path.join(__dirname, '../public/uploads') }) module.exports = upload
multer().single(fieldname), will be from req Accept a file named fieldname in file. Fieldname is the field name of the uploaded file. Look at the figure
Modify server JS is as follows
const express = require('express') const app = express() const bodyParser = require('body-parser') const uploadMiddleware = require('./middlewares/upload-teach') app.use(bodyParser.urlencoded({ extended: false })) const uploadControl = (req, res, next) => { console.log('req.file', req.file); res.send('File uploaded successfully') } // In req Receive the file with fieldname file in file app.use('/api/files/upload', uploadMiddleware.single('file'), uploadControl) app.listen(3000, () => { console.log('localhost:3000'); })
Now you can upload files on the front page
After uploading, you can see that there is one more file in the public/uploads directory of the server
But it's amazing that it doesn't have an extension. We try to give it an extension manually
The picture can be displayed normally, so next we have to find a way to add the correct extension to the picture before it is stored
We use mime to get the file extension
npm i mime -S
const mime = require('mime'); mime.getType('txt'); // ⇨ 'text/plain' mime.getExtension('text/plain'); // ⇨ 'txt'
storage
Two options are available, destination and filename. They are all functions used to determine where files are stored.
-
Destination is used to determine which folder the uploaded file should be stored in. You can also provide a string (for example '/ tmp/uploads'). If destination is not set, the default temporary folder of the operating system is used.
-
Filename is used to modify the file name in the folder. If filename is not set, each file will be set to a random file name with no extension.
Modify middlewares / upload js
const path = require('path') const multer = require('multer') const mime = require('mime') let filename = '' // Define file storage path and file name const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, path.join(__dirname, '../public/uploads')) }, filename: function (req, file, cb) { console.log('file', file); const ext = mime.getExtension(file.mimetype); filename = `${file.fieldname}-${Date.now()}.${ext}` cb(null, filename) } }) const upload = multer({ storage }) module.exports = upload
Re upload the file at the front end, and you can see that the file has its own extension
What's next? Do what?
limits
Limits is an object that specifies some data size limits
You can use the following:
Key | Description | Default |
---|---|---|
fieldNameSize | Maximum length of field name | 100 bytes |
fieldSize | Maximum length of field value | 1MB |
fields | Maximum number of non file field s | infinite |
fileSize | In multipart form, the maximum length of the file (in bytes) | infinite |
files | Maximum number of files in multipart form | infinite |
parts | The maximum number of part transfers in a multipart form (fields + files) | infinite |
headerPairs | The maximum number of key value pairs in a multipart form | 2000 |
Setting limits can help protect your site from denial of service (DoS) attacks.
At middlewares / upload JS add the following content
// ... // Limit some data size of uploaded files const limits = { fileSize: 200 * 1000, // 200kb files: 1, } const upload = multer({ // ... limits })
Re upload files on the front end,
When the file size is less than 200k, the file is uploaded successfully;
However, when the file size exceeds 200k, the console prints the following
We expect to catch this error and respond to it to the front end. What should we do?
We will solve this problem later. At least we have achieved O(∩∩) O ha for the limitation of file data~
fileFilter
Set a function to control which files can be uploaded and which files should be skipped. This function should look like this:
function fileFilter (req, file, cb) { // This function should call 'cb' with a boolean value // Indicates whether the file should be accepted // Reject this file and use 'false', like this: cb(null, false) // Accept this 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!')) }
At middlewares / upload JS add the following content
// ... // filter const fileFilter = (req, file, cb) => { // This function should call 'cb' with a boolean value // Indicates whether the file should be accepted // Accepted file types const accessTypes = ['image/png', 'image/jpg', 'image/jpeg', 'image/gif'] if (!accessTypes.includes(file.mimetype)) { cb(new Error('File type must be .png,.jpg,.gif')) } else { // Accept this file and use ` true` cb(null, true) } } const upload = multer({ // ... fileFilter })
Re upload files at the front end
When the file is png .jpeg .gif, the file is uploaded successfully
However, when the file is of other file types, you can see that the server console prints the following contents
We still expect to catch this error and respond to it to the front end
OK, then let's solve the problem of capturing errors above
Error handling mechanism
When an error is encountered, multer will send the error to express. You can use a better error display page( express standard mode).
If you want to catch the error sent by multer, you can call the middleware program yourself. If you want to capture Multer error , you can use the MulterError class under the multer object (namely err instanceof multer.MulterError).
If you observe carefully, you can find that the err types of the above two error pictures are inconsistent
Through this discovery, we can have the basis to respond to the two errors to the front end respectively
We will middlewares / upload JS is modified to manually call the middleware program, and the following method is changed
const multer = require('multer') const upload = multer().single('avatar') app.post('/profile', function (req, res) { upload(req, res, function (err) { if (err instanceof multer.MulterError) { // A MulterError error occurred } else if (err) { // js Error occurred } // everything goes well }) })
At middlewares / upload JS add the following content
// ... const upload = multer({ storage, limits, fileFilter }).single('file') const uploadMiddleware = (req, res, next) => { upload(req, res, (err) => { res.set('Content-Type', 'application/json; charset=utf-8') if (err instanceof multer.MulterError) { // A MulterError error occurred console.log('MulterError', err.message); if (err.message.includes('File too large')) { res.send({ code: 10000, message: `The file is too large for ${limits.fileSize / 1000} kb la` }) } else { res.send({ code: 10000, message: err.message }) } } else if (err) { // js Error occurred res.send({ code: 10000, message: err.message }) } // everything goes well next() }) }
Modify server js
const express = require('express') const app = express() const bodyParser = require('body-parser') const uploadMiddleware = require('./middlewares/upload-teach') app.use(bodyParser.urlencoded({ extended: false })) const uploadControl = (req, res, next) => { // console.log('req.file', req.file); res.send('File uploaded successfully') } app.use('/api/files/upload', uploadMiddleware, uploadControl) app.listen(3000, () => { console.log('localhost:3000'); })
Upload the file again and you can see that everything is normal!
There is also a small problem. How can the stored files be transferred to the next middleware?
We will mount req with the obtained filename On the body, this can be transmitted to the next Middleware in one direction
Server complete code
server.js
const express = require('express') const app = express() const bodyParser = require('body-parser') const uploadMiddleware = require('./middlewares/upload-teach') app.use(bodyParser.urlencoded({ extended: false })) const uploadControl = (req, res, next) => { // console.log('req.file', req.file); res.send('File uploaded successfully') } app.use('/api/files/upload', uploadMiddleware, uploadControl) app.listen(3000, () => { console.log('localhost:3000'); })
middlewares/upload.js
const path = require('path') const multer = require('multer') const mime = require('mime') let filename = '' // Define file storage path and file name const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, path.join(__dirname, '../public/uploads')) }, filename: function (req, file, cb) { console.log('file', file); const ext = mime.getExtension(file.mimetype); filename = `${file.fieldname}-${Date.now()}.${ext}` cb(null, filename) } }) // Limit some data size of uploaded files const limits = { fileSize: 200 * 1000, // 200kb files: 1, } // filter const fileFilter = (req, file, cb) => { // This function should call 'cb' with a boolean value // Indicates whether the file should be accepted // Accepted file types const accessTypes = ['image/png', 'image/jpg', 'image/jpeg', 'image/gif'] if (!accessTypes.includes(file.mimetype)) { cb(new Error('File type must be .png,.jpg,.gif')) } else { // Accept this file and use ` true` cb(null, true) } } const upload = multer({ storage, limits, fileFilter }).single('file') const uploadMiddleware = (req, res, next) => { upload(req, res, (err) => { res.set('Content-Type', 'application/json; charset=utf-8') if (err instanceof multer.MulterError) { // A MulterError error occurred console.log('MulterError', err.message); if (err.message.includes('File too large')) { res.send({ code: 10000, message: `The file is too large for ${limits.fileSize / 1000} kb la` }) } else { res.send({ code: 10000, message: err.message }) } } else if (err) { // js Error occurred res.send({ code: 10000, message: err.message }) } // everything goes well next() }) } module.exports = uploadMiddleware
Well, if this article is useful to you, please don't hesitate to praise and collect o( ̄)  ̄) O
Source code: https://gitee.com/yanhuakang/nodejs-demos/tree/master/codes/express/05-%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0