nodejs-upload-files-trungquandev

Upload files trong Node.js?

Xin chào tất cả các bạn, mình là Quân, tiếp tục series về Node.js, hôm nay chúng ta sẽ cùng nhau đi tìm hiểu hai cách để upload files trong ứng dụng Node.js nhé.

“Bài này thuộc bài số 06 trong loạt bài Lập Trình Nodejs Cơ Bản.

Những nội dung có trong bài:

  1. Tạo sẵn giao diện Form Upload
  2. Xử lý Upload file với Formidable
  3. Xử lý Upload file với Multer
  4. Full source code trên github

1. Tạo sẵn giao diện Form Upload

Phần này vì mình đã hướng dẫn ở bài Tìm hiểu 3 Modules Built-in trong Node.js: HTTP – URL – File System nên mình sẽ không giải thích nhiều mà paste code vào luôn thôi nhé:

Cấu trúc thư mục ứng dụng như sau:

uploads

views

master.html

package.json

server.js

  • Nội dung file server.js
/**
 * Created by trungquandev.com's author on 10/03/2019.
 * server.js
 */
 
let http = require("http");
let url = require("url");
let fs = require("fs");
 
let server = http.createServer((req, res) => {
    let urlData = url.parse(req.url, true);
    let fileName = "./views" + urlData.pathname;
 
    if(urlData.pathname === "/") {
        fileName = "./views/master.html";
    }
 
    fs.readFile(fileName, (err, data) => {
        if(err) {
            console.log(err);
            res.writeHead(404, {"Content-Type": "text/html"});
            res.write("404 Not Found");
 
            return res.end();
        }
        res.writeHead(200, {"Content-Type": "text/html"});
        res.write(data);
 
        return res.end();
    });
});
 
server.listen(8017, "localhost", () => {
  console.log(`Hello Trung Quan, I'm running at localhost:8017/`);
});
  • Nội dung file master.html
<!--
* Created by trungquandev.com's author on 10/03/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 upload files</title>

    <!-- Get bootstrap from CDN-->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-sm-8">
                <br>
                <h4>
                    Node.js upload files - trungquandev
                </h4>
                <div class=""></div>
                <form action="/upload" method="POST" enctype="multipart/form-data">
                    <div class="form-group">
                        <label for="example-input-file">&nbsp;</label>
                        <input type="file" name="file" class="form-control-file border">
                    </div>
                    <button type="submit" class="btn btn-primary">Submit</button>
                </form>
            </div>
        </div>
    </div>
</body>
</html>

“Lưu ý là trong thẻ <form> ở trên phải có một thuộc tính là enctype=”multipart/form-data” và thẻ <input> phải có một cái name ví dụ như name=”file” ở trên nhé.”

Kết quả sau khi chạy ứng dụng: node server.js

nodejs-upload-files-trungquandev-01

2. Xử lý Upload file với Formidable

Nói qua về thằng này một chút, mình trích lời giới thiệu chính thức của Formidable thì nó là một module giúp chúng ta phân tích dữ liệu biểu mẫu, đặc biệt là tập trung vào việc encoding các hình ảnh và video, hay nói dễ hiểu và ngắn gọn hơn là tập trung vào việc tải lên các tệp tin.

Một số tính năng mà module này liệt kê ra như sau:

  • Non-buffering multipart parser: Tốc độ cao, lên tới xấp xỉ 500mb/giây và không cần bộ nhớ đệm cho việc phân tích cú pháp.
  • Automatically writing file uploads to disk: Tự động ghi lại các tập tin tải lên.
  • Graceful error handling: Cú pháp bắt lỗi & xử lý lỗi dễ dàng. 
  • Low memory footprint: Có thể hiểu đơn giản là thư viện này ăn ít Ram.
  • Very high test coverage: Thư viện này đã được viết test rất cẩn thận (theo như chúng nó nói =))

Rồi, xem qua lý thuyết như vậy thôi, mình sẽ bắt đầu triển khai một ví dụ để các bạn biết cách dùng module này cho việc upload nhé:

“Cụ thể hơn, trong ví dụ dưới đây, mình sẽ sử dụng các module http-fs-url để tạo server, và kết hợp dùng formidable để xử lý upload file. Chưa dùng framework express.js nhé, express.js để phần 3 mình sẽ làm ví dụ với multer.”

Cài đặt module: npm install --save formidable hoặc npm i -S formidable

Ở file server.js có 2 việc cần làm:

  • Việc đầu tiên là chúng ta nạp module formidable:
  • Việc thứ hai là viết thêm một đoạn code kiểm tra điều kiện upload file bên trong function createServer()

– Nội dung file server.js sau khi thay đổi như sau (mình đã comment giải thích rõ ràng những đoạn code mới thêm vào rồi nha)

/**
 * Created by trungquandev.com's author on 10/03/2019.
 * server.js
 */

let http = require("http");
let url = require("url");
let fs = require("fs");
let formidable = require("formidable");
 
let server = http.createServer((req, res) => {
    // Kiểm tra nếu như url truyền lên là /upload và phương thức là post
    if (req.url == "/upload" && req.method.toLowerCase() == "post") {
      // Khởi tạo biến form bằng IncomingForm để phân tích một tập tin tải lên
      let form = new formidable.IncomingForm();

      // Cấu hình thư mục sẽ chứa file trên server với hàm .uploadDir
      form.uploadDir = "uploads/"

      // Xử lý upload file với hàm .parse
      form.parse(req, (err, fields, files) => {
        if (err) throw err;
        // Lấy ra đường dẫn tạm của tệp tin trên server
        let tmpPath = files.file.path;
        // Khởi tạo đường dẫn mới, mục đích để lưu file vào thư mục uploads của chúng ta
        let newPath = form.uploadDir + files.file.name;
        // Đổi tên của file tạm thành tên mới và lưu lại
        fs.rename(tmpPath, newPath, (err) => {
          if (err) throw err;
          
          switch (files.file.type) {
            // Kiểm tra nếu như là file ảnh thì render ảnh và hiển thị lên.
            case "image/jpeg":
              fs.readFile(newPath, (err, fileUploaded) => {
                res.writeHead(200,{"Content-type":"image/jpeg"});
                res.end(fileUploaded);
              });
              break;
            // Còn lại các loại file khác thì chỉ hiển thị nội dung thông báo upload thành công.
            default:
              res.writeHead(200, {"Content-Type": "text/html"});
              res.end(`Upload file <strong>${files.file.name}</strong> successfuly`);
              break;
          }
        });
      });

      // Return ở đây để code không chạy tiếp xuống dưới
      return;
    }

    // Nếu không phải action upload thì trả về client cái form upload
    let urlData = url.parse(req.url, true);
    let fileName = "./views" + urlData.pathname;
    if(urlData.pathname === "/") {
        fileName = "./views/master.html";
    }
    fs.readFile(fileName, (err, data) => {
      if(err) {
        console.log(err);
        res.writeHead(404, {"Content-Type": "text/html"});
        res.write("404 Not Found");
        return res.end();
      }
      res.writeHead(200, {"Content-Type": "text/html"});
      res.write(data);
      return res.end();
    });
});
 
server.listen(8017, "localhost", () => {
  console.log(`Hello Trung Quan, I'm running at localhost:8017/`);
});

Kết quả: Một bức tranh con mèo do mình vẽ, cho mình khoe chút =))))

Upload ảnh thành công:

nodejs-upload-files-trungquandev-02

Upload tệp tin trungquandev.txt thành công:

nodejs-upload-files-trungquandev-03

Kiểm tra trong thư mục:

nodejs-upload-files-trungquandev-04

Ngoài ra module này mở rộng thêm nhiều tính năng nữa ví dụ như giới hạn kích thước file tải lên form.maxFileSize…vv, thì có thể xem thêm ở đây nhé: Formidable – npm


3. Xử lý Upload file với Multer

Multer cũng là một module khá nổi tiếng, tuy lượt dowload hàng tuần rất khiêm tốn là (421,391 lượt) thấp hơn so với formidable (2,206,310 lượt) nhưng đổi lại thì số star trên repo github của nó lại cao hơn (6192 star) so với (4849 star) của formidable.
“Lưu ý số liệu này là tính đến thời điểm hiện tại mình viết bài, sau này có thể sẽ khác.”

Một điểm cộng nữa là Multer có thể sử dụng dễ dàng như một middleware đối với một ứng dụng node.js chạy trên framework Express.js, chắc nhờ fame thằng Express này nên số star của nó cũng cao, bởi vì rất nhiều người mới đầu tiếp cận với node.js cũng đều học Express.js đầu tiên. (Mình là một điển hình 😀 )

Bây giờ mình sẽ làm ví dụ upload file sử dụng kết hợp Multer và Express.js nhé.
(Dĩ nhiên là các bạn cũng có thể kết hợp Formidable với Express.js hay là Multer với http-fs-url như ở phần 2 nhé, cần support thì cứ thoải mái comment hỏi mình 😀 )

Trước tiên, các bạn cài cho mình 2 thằng expressmulter:

npm install --save express

npm install --save multer

Ngoài ra mình có sử dụng thêm một module tích hợp sẵn trong node.js là “path” để lấy đường dẫn gốc của thư mục.
Cụ thể, chúng ta sẽ viết lại file server.js như sau:

/**
 * Created by trungquandev.com's author on 11/03/2019.
 * server.js
 */

let express = require("express");
let multer = require("multer");
let path = require("path");

let app = express();

// Route này trả về cái form upload cho client
app.get("/", (req, res) => {
  res.sendFile(path.join(`${__dirname}/views/master.html`));
});

// Khởi tạo biến cấu hình cho việc lưu trữ file upload
let diskStorage = multer.diskStorage({
  destination: (req, file, callback) => {
    // Định nghĩa nơi file upload sẽ được lưu lại
    callback(null, "uploads");
  },
  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 để đảm bảo không bị trùng.
    let filename = `${Date.now()}-trungquandev-${file.originalname}`;
    callback(null, filename);
  }
});

// Khởi tạo middleware uploadFile với cấu hình như ở trên,
// Bên trong hàm .single() truyền vào name của thẻ input, ở đây là "file"
let uploadFile = multer({storage: diskStorage}).single("file");

// Route này Xử lý khi client thực hiện hành động upload file
app.post("/upload", (req, res) => {
  // Thực hiện upload file, truyền vào 2 biến req và res
  uploadFile(req, res, (error) => {
    // Nếu có lỗi thì trả về lỗi cho client.
    // Ví dụ như upload một file không phải file ảnh theo như cấu hình của mình bên trên
    if (error) {
      return res.send(`Error when trying to upload: ${error}`);
    }
    // Không có lỗi thì lại render cái file ảnh về cho client.
    // Đồng thời file đã được lưu vào thư mục uploads
    res.sendFile(path.join(`${__dirname}/uploads/${req.file.filename}`));
  });
});
 
app.listen(8017, "localhost", () => {
  console.log(`Hello Trung Quan, I'm running at localhost:8017/`);
});

Kết quả:Khoe nốt một bức tranh mùa đông nữa của mình, đừng cười nhé =)))))

Upload thành công:

nodejs-upload-files-trungquandev-05

Khi upload lỗi:

nodejs-upload-files-trungquandev-06

Kiểm tra trong thư mục:

nodejs-upload-files-trungquandev-07

Các bạn cũng có thể đọc thêm về multer nhé, mình chỉ hướng dẫn cơ bản thôi, đi sâu vào nó còn nhiều tính năng hơn nữa ở đây:
https://www.npmjs.com/package/multer

4. Full source code trên github

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, chân thành cảm ơn các bạn.
https://github.com/trungquan17/multer-nodejs-upload-file


Trên đây mình đã hướng dẫn upload file trong node.js bằng 2 cách, nhưng như các bạn thấy là chúng ta mới chỉ đang upload một lần một file thôi. Bài tiếp theo mình sẽ xử lý việc Upload nhiều file cùng lúc trong node.js nhé.

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/formidable

https://www.npmjs.com/package/multer

“Thanks for awesome knowledges.”

trungquandev-img-modal

Khóa học lập trình làm việc thực tế:

Nếu các bạn thấy bài viết của mình có ích thì hãy ủng hộ mình bằng cách tham khảo bài viết giới thiệu khóa học cực kỳ chất lượng và chính chủ dưới đây của mình nhé, cảm ơn các bạn ^^

nodejs-mongodb-messenger-realtime-course-trungquandev
Node.js và MongoDB - Xây dựng một ứng dụng Messenger trò chuyện trực tuyến.