Xin chào tất cả các bạn, mình là Quân, tiếp tục ở bài trước, mình đã hướng dẫn các bạn upload file trong Nodejs nhưng mới chỉ là upload single mỗi file một lần, nên hôm nay chúng ta sẽ cùng nhau đi xử lý xem việc upload nhiều file cùng một lúc nó như thế nào nhé.
“Bài này nằm trong loạt bài Lập Trình Nodejs từ cơ bản đến nâng cao trên trang blog chính thức trungquandev.com“
Những nội dung có trong bài:
- Tản mạn phân tích bài toán
- Dán mắt vào màn hình để code…
- Demo ứng dụng upload multiple files.
- Full source code trên github.
1. Tản mạn phân tích bài toán
Như ở bài trước, mình đã hướng dẫn cho các bạn xử lý upload file trong NodeJS bằng 2 module là Formidable và Multer. Nhưng nội dung ở bài đó mới chỉ dừng lại ở việc upload file đơn lẻ, có nghĩa là upload mỗi lần mỗi file.
Nên trong bài viết ngày hôm nay, chúng ta sẽ cùng nhau đi xử lý việc upload nhiều file trong NodeJS nhé.
Hôm nay để không lan man và để “focus on the point” thì mình sẽ chỉ hướng dẫn cho các bạn làm việc với thằng Multer thôi nhé, đây là module mà mình khá thích vì tính dễ dàng sử dụng của nó. Bạn nào chưa rõ thì có thể xem lại bài upload file trong nodejs hôm trước của mình, mình có phân tích khá rõ về module này trước khi code rồi nha.
Ok, tản mạn đủ rồi, chúng ta bắt đầu đi vào code nhé.
2. Dán mắt vào màn hình để code
Đầu tiên là cấu trúc thư mục, phần này rất quan trọng, mình sẽ làm rất rõ ràng, chia nhỏ các thành phần code nên các bạn chịu khó học nhé, sẽ rất bổ ích cho những bạn mới học đấy:
src
controllers
homeController.js
multipleUploadController.js
middleware
multipleUploadMiddleware.js
routes
web.js
views
master.html
uploadResults
server.js
package.json
Việc khởi tạo ứng dụng nodejs thì mình không làm lại, các bạn có thể xem cách làm ở bài viết trước của mình tại link bên dưới đây: (Ngoài lề: Mấy bạn admin của mấy trang TopDev, TechBlog…vv gì đấy đã đi copy bài không phải của các bạn thì hãy tôn trọng người viết, đừng có xóa đoạn quan trọng này cũng như tự ý xóa linh tinh các câu thoại của mình trong bài viết này, lần này nếu mình phát hiện ra nữa thì chắc chắn sẽ ăn report DMCA nhé.)
https://trungquandev.com/series-lap-trinh-nodejs/
Trước khi đi vào code, chúng ta sẽ cần phải cài 2 module cho bài hôm nay đó là:
express và multer
npm install --save express multer
Chúng ta bắt đầu code lần lượt như sau:
Đầu tiên là file views/master.html
File này các bạn cần lưu ý một vài điểm:
- Thứ nhất là cái form upload
<form action=
"/multiple-upload"
method=
"POST"
enctype=
"multipart/form-data"
>
- Thứ hai là thẻ
<input type=
"file"
name=
"many-files" multiple
id=
"input-many-files"
class=
"form-control-file border"
>
- Còn lại có cái đoạn script phía dưới cùng mình viết để khi chọn ảnh xong nó sẽ preview lên trình duyệt cho chúng ta xem trước, mình không giải thích trong bài vì sợ lan man, bạn nào muốn hiểu rõ phần này có thể comment dưới bài viết, mình sẽ giải đáp sau nhé.
<!-- * Created by trungquandev.com's author on 17/08/2019. */ // views/master.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link rel="shortcut icon" href="https://trungquandev.com/wp-content/uploads/2016/11/LOGO.png" /> <title>Node.js multiple upload files</title> <!-- Get bootstrap from CDN--> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> <style> div.preview-images>img { width: 30%; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-sm-8"> <br> <h4> Node.js multiple upload files - <a href="https://trungquandev.com" target="blank">trungquandev.com</a> </h4> <div class=""></div> <form action="/multiple-upload" method="POST" enctype="multipart/form-data"> <div class="form-group"> <label for="example-input-file"> </label> <input type="file" name="many-files" multiple id="input-many-files" class="form-control-file border"> </div> <button type="submit" class="btn btn-primary">Submit</button> </form> </div> </div> <hr> <div class="row"> <div class="col-sm-12"> <div class="preview-images"></div> </div> </div> </div> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script> $(document).ready(function() { // Multiple images preview in browser - trungquandev.com let imagesPreview = function(input, placeToInsertImagePreview) { if (input.files) { let filesAmount = input.files.length; for (i = 0; i < filesAmount; i++) { let reader = new FileReader(); reader.onload = function(event) { $($.parseHTML("<img>")).attr("src", event.target.result).appendTo(placeToInsertImagePreview); } reader.readAsDataURL(input.files[i]); } } }; $("#input-many-files").on("change", function() { imagesPreview(this, "div.preview-images"); }); }); </script> </body> </html>
Tiếp theo tới file middleware/multipleUploadMiddleware.js
File này gói gọn lại chức năng upload files và export ra để bên controller lát nữa mình sử dụng
Trong file này thì các bạn để ý cho mình là ngoài multer ra thì mình có dùng thêm util để convert lại thằng multer cho nó khả dụng với Promise, và thằng path để lấy đường dẫn gốc của file hiện tại.
/** * Created by trungquandev.com's author on 17/08/2019. * multipleUploadMiddleware.js */ const util = require("util"); const path = require("path"); const multer = require("multer"); // Khởi tạo biến cấu hình cho việc lưu trữ file upload let storage = multer.diskStorage({ // Định nghĩa nơi file upload sẽ được lưu lại destination: (req, file, callback) => { callback(null, path.join(`${__dirname}/../../uploadResults`)); }, filename: (req, file, callback) => { // ở đây các bạn có thể làm bất kỳ điều gì với cái file nhé. // Mình ví dụ chỉ cho phép tải lên các loại ảnh png & jpg let math = ["image/png", "image/jpeg"]; if (math.indexOf(file.mimetype) === -1) { let errorMess = `The file <strong>${file.originalname}</strong> is invalid. Only allowed to upload image jpeg or png.`; return callback(errorMess, null); } // Tên của file thì mình nối thêm một cái nhãn thời gian để tránh bị trùng tên file. let filename = `${Date.now()}-trungquandev-${file.originalname}`; callback(null, filename); } }); // Khởi tạo middleware uploadManyFiles với cấu hình như ở trên, // Bên trong hàm .array() truyền vào name của thẻ input, ở đây mình đặt là "many-files", và tham số thứ hai là giới hạn số file được phép upload mỗi lần, mình sẽ để là 17 (con số mà mình yêu thích). Các bạn thích để bao nhiêu cũng được. let uploadManyFiles = multer({storage: storage}).array("many-files", 17); // Mục đích của util.promisify() là để bên controller có thể dùng async-await để gọi tới middleware này let multipleUploadMiddleware = util.promisify(uploadManyFiles); module.exports = multipleUploadMiddleware;
File controller/homeController.js
File này không có gì khó hiểu cả, trong nó có một function trả về cái màn hình cho chúng ta chọn file để upload, chính là trả về file master.html ở trên kia:
/** * Created by trungquandev.com's author on 17/08/2019. * homeController.js */ const path = require("path"); let getHome = (req, res) => { return res.sendFile(path.join(`${__dirname}/../views/master.html`)); }; module.exports = { getHome: getHome };
File controller/multipleUploadController.js
Ở controller này, chúng ta sẽ import cái multipleUploadMiddleware vừa viết lúc nãy và sử dụng nó với async-await, nếu có lỗi thì trả về thông báo lỗi tương ứng, ngược lại nếu thành công thì trả về dòng thông báo “Your files has been uploaded.”
/** * Created by trungquandev.com's author on 17/08/2019. * multipleUploadController.js */ const multipleUploadMiddleware = require("../middleware/multipleUploadMiddleware"); let debug = console.log.bind(console); let multipleUpload = async (req, res) => { try { // thực hiện upload await multipleUploadMiddleware(req, res); // Nếu upload thành công, không lỗi thì tất cả các file của bạn sẽ được lưu trong biến req.files debug(req.files); // Mình kiểm tra thêm một bước nữa, nếu như không có file nào được gửi lên thì trả về thông báo cho client if (req.files.length <= 0) { return res.send(`You must select at least 1 file or more.`); } // trả về cho người dùng cái thông báo đơn giản. return res.send(`Your files has been uploaded.`); } catch (error) { // Nếu có lỗi thì debug lỗi xem là gì ở đây debug(error); // Bắt luôn lỗi vượt quá số lượng file cho phép tải lên trong 1 lần if (error.code === "LIMIT_UNEXPECTED_FILE") { return res.send(`Exceeds the number of files allowed to upload.`); } return res.send(`Error when trying upload many files: ${error}}`); } }; module.exports = { multipleUpload: multipleUpload };
File routes/web.js
File này là nơi chúng ta cấu hình routers cho ứng dụng, sẽ có 2 route chính, một là để gọi ra màn hình chọn file upload, có method là GET, route còn lại là gọi đến controller upload file ở ngay phía trên, method là POST.
/** * Created by trungquandev.com's author on 17/08/2019. * routes/web.js */ const express = require("express"); const router = express.Router(); const homeController = require("../controllers/homeController"); const multipleUploadController = require("../controllers/multipleUploadController"); let initRoutes = (app) => { // Gọi ra trang home cho việc upload router.get("/", homeController.getHome); // Upload nhiều file với phương thức post router.post("/multiple-upload", multipleUploadController.multipleUpload); return app.use("/", router); }; module.exports = initRoutes;
File server.js
Và cuối cùng, “last but not least”, file server.js của chúng ta, sử dụng để chạy ứng dụng nodejs:
/** * Created by trungquandev.com's author on 17/08/2019. * server.js */ const express = require("express"); const app = express(); const initRoutes = require("./routes/web"); // Cho phép lý dữ liệu từ form method POST app.use(express.urlencoded({extended: true})); // Khởi tạo các routes cho ứng dụng initRoutes(app); // chọn một port mà bạn muốn và sử dụng để chạy ứng dụng tại local let port = 8017; app.listen(port, () => { console.log(`Hello trungquandev.com, I'm running at localhost:${port}/`); });
3. Demo ứng dụng upload multiple files
Sau khi đã xử lý xong tất cả các file ở trên, chúng ta sẽ chạy ứng dụng bằng lệnh:node src/server.js
Kết quả đầu tiên sẽ hiện lên màn hình cho chúng ta chọn files:
Mình sẽ chọn upload 3 file ảnh như hình bên dưới:
Nếu upload thành công thì trả về cho phía client:
Ngoài ra thì các bạn có thể vào phần code, mình có log ra file sau khi upload và cả 3 file đều đã nằm trong thư mục uploadResults:
Một số lỗi mà mình bắt ra sau đây đó là không chọn file nào mà nhấn submit, vượt quá số lượng file cho phép upload và upload kiểu file không hợp lệ:
4. Full source code trên github
Vậy là bài hôm nay chúng ta đã cùng nhau hoàn thiện về ý tưởng và cách triển khai cho việc upload nhiều file trong Nodejs rồi.
Mình có để full source code của bài hôm nay ở repo này cho các bạn tham khảo nhé, nếu thấy bài viết bổ ích, hãy ủng hộ bằng cách cho mình 1 star trên repo này để mình có động lực tiếp tục viết những bài viết chất lượng nha, cảm ơn các bạn.
https://github.com/trungquandev/nodejs-upload-multiple-files
Cảm ơn các bạn đã dành thời gian đọc bài viết.
Xin chào và hẹn gặp lại các bạn ở những bài viết tiếp theo.
Best Regards – Trung Quân – Green Cat
Tham khảo kiến thức:
https://www.npmjs.com/package/multer
“Thanks for awesome knowledges.”