Thứ Sáu, 16 tháng 8, 2013

Streams trong nodejs

Phần 3: Streams

1. Stream là gì?
Stream là dòng chảy. Nó là quá trình truyền tải dữ liệu từ client đến server hoặc ngược lại. Stream có thể là đọc, ghi hoặc cả 2. Ví dụ như khi chúng ta upload 1 file lên mạng, server sẽ đọc dữ liệu từ client gửi lên và thông báo tiến trình upload được bao nhiêu phần trăm, đó chính là khả năng đọc và ghi của stream.
var http = require('http'); // đây là cách chúng ta require các modules

http.createServer(function(request, response){

    response.writeHead(200, {'Content-Type':'text/plain'}); // Status code và content type

    response.write("Xin chào lập trình viên!"); // Thông điệp được gửi xuống client.

    response.end(); // Đóng kết nối

}).listen(3000); // Chờ kết nối ở cổng 3000.

console.log("Server đang chờ kết nối tại cổng 3000");
Trong ví dụ trên, tham số trong callback là request chính là luồng đọc của stream, response là luồng ghi của stream.
2. Đọc dữ liệu từ 1 request
Đối tượng Readable Stream emit 2 event là data và end.
stream
Chúng ta sẽ xem dữ liệu gửi từ client lên được xử lý như thế nào:
var http = require('http');

http.createServer(function(req, res) {

   res.writeHead(200);

   req.on('data', function(chunk) {

      console.log(chunk.toString());

   });

   req.on('end', function() {

      res.end();

   });

}).listen(8080);
Đoạn code trên khá đơn giản, server nhận dữ liệu từ client gửi lên và echo ra dữ liệu. Tham số chunk là phần dữ liệu mà server nhận được thông qua việc chờ event data. Các bạn copy đoạn code trên vào 1 file, giả sử là stream.js sau đó chạy lệnh node stream.js trên terminal. Mở thêm 1 tab trên terminal và chạy lệnh curl -'Hello world' http://localhost:8080. Kiểm tra bên tab đang chạy node bạn sẽ thấy kết quả.
Quá trình mà server xử lý event data và end, node.js cung cấp cho ta 1 phương thức gọn hơn:
var http = require('http');

http.createServer(function(req, res) {

   res.writeHead(200);

   req.pipe(res);

}).listen(8080);
Chạy lệnh curl -'Hello world' http://localhost:8080 bạn sẽ thấy server trả về kết quả ngay khi bạn chạy lệnh curl.
3. Upload file
Để upload file, chúng ta cần require module filesystem vào chương trình.
var http = require('http'),
    fs = require('fs');

http.createServer(function(req, res) {

   res.writeHead(200);

   var newfile = fs.createWriteStream('myfile.txt');

   req.on('data', function(chunk) {

      newfile.write(chunk);

   });

   req.on('end', function() {

      res.end('Uploaded!');

   });

}).listen(8080);
hoặc sử dụng phương thức pipe:
var http = require('http'),
    fs = require('fs');

http.createServer(function(req, res) {

   res.writeHead(200);

   var newfile = fs.createWriteStream('myfile.txt');

   req.pipe(newfile);

   req.on('end', function() {

      res.end('Uploaded!');

   });

}).listen(8080);
Phương thức createWriteStream sẽ cho phép chúng ta ghi dữ liệu ra file. Tham số của phương thức là tên file mà chúng ta muốn lưu. Chúng ta tạo 1 file trong cùng thư mục, giả sử là test_upload.txt, sau đó thêm 1 số text tuỳ ý và save lại. Chạy lênhcurl --upload-file test_upload.txt http://localhost:8080 bạn sẽ thấy chương trình tạo ra 1 file myfile.txt trong cùng thư mục với nội dung giống hệt với file test_upload.txt của bạn.
Thông thường, khi bạn đọc ghi 1 file thì luồng ghi sẽ chậm hơn luồng đọc, trong trường hợp server đọc dữ liệu và liên tục push thì buffer sẽ tràn và không nhận dữ liệu thêm được nữa, hiện tượng này được gọi là backpressure, tức là áp lực ngược. Một ví dụ thực tế là khi bạn rót nước vào 1 chiếc bình thông qua 1 cái phễu, nếu bạn đổ nước quá nhanh mà nước không xuống bình kịp do miệng bình hẹp thì nước sẽ bị tràn ra ngoài. Việc đọc ghi file cũng tương tự như vậy. Và với phương thức pipe mà mình giới thiệu ở trên, vấn đề này đã được khắc phục. Khi bạn sử dụng phương thức pipe để đọc ghi file, nội tại nó sẽ biết được khi nào thì luồng ghi đang đầy, nó sẽ dừng luồng đọc lại. Quá trình đó lặp lại đến khi file được đọc và ghi thành công. Qúa trình này được mô tả qua đoạn code sau:
readStream.on('data', function(chunk) {

   var buffer = writeStream.write(chunk); // return false nếu buffer full.

   if (!buffer) readStream.pause();

});

writeStream.on('drain', function() {

   readStream.resume();

});
Diễn giải thì dài dòng thế nhưng chúng ta chỉ cần đúng 1 dòng code:
readStream.pipe(writeStream);
4. Thể hiện quá trình upload file
Chúng ta có thể thể hiện quá trình upload file thông qua độ dài từng chunk dữ liệu với tổng độ dài cả file. Ta sẽ sửa lại đoạn code ở phần 3 một chút:
var http = require('http'),

   fs = require('fs');

http.createServer(function(req, res) {

   res.writeHead(200);

   var newfile = fs.createWriteStream('myfile.txt');

   var fileLength = req.headers['content-length']; // Độ dài của file upload.

   var uploadedBytes = 0;

   req.pipe(newfile);

   req.on('data', function(chunk) {

      uploadedBytes += chunk.length;

      var progress = uploadedBytes / fileLength * 100; // Phần trăm upload file.

      res.write('Uploaded: ' + parseInt(progress, 10) + '% \n'); // Show tiến trình upload file.

   });

   req.on('end', function() {

      res.end('Your file was uploaded successful!');

   });

}).listen(8080);
Sử dụng lệnh curl như trên để test trường hợp này. Bạn nên test thử với file dung lượng lớn 1 chút vì test trên localhost tiến trình chạy cũng khá nhanh.
Vậy là chúng ta đã tìm hiểu xong cách thức node.js xử lý upload file và kết thúc phần 3 của series Nhập môn Nodejs với tại đây. Các bạn có thể xem lại phần 1 và phần 2 mình đã post để tiện theo dõi series này nhé. Phần tiếp theo chúng ta sẽ đi sâu vào vấn đề module trong node.js và tìm hiểu cách tạo 1 module node.js.

Không có nhận xét nào:

Đăng nhận xét