10

Hướng dẫn tạo ứng dụng chia sẻ hình ảnh với Nodejs, Socket.io và ExpressJs

Bài viết này mình sẽ hướng dẫn các bạn tạo ứng dụng chia sẻ ảnh real-time, ảnh gởi sẽ hiển thị ngay trực tiếp đến địa chỉ hiển thị. Sử dụng công nghệ thời thượng websocket, chúng ta có khả năng tạo ra những ứng dụng có khả năng hồi đáp và phản ứng tức thời với hành động người dùng, cũng như việc trao đổi dữ liệu ngay tức khắc mà hầu như chỉ ảnh hưởng bởi băng thông.

Yêu cầu:

  • Bạn đã cài đặt Nodejs. Về Nodes bạn nào chưa biết thì tự tìm hiểu thêm, mình sẽ có 1 bài giới thiệu sau.
  • Text editor  như Notepad++, Sublime, Komodo Edit… hay Vim, tùy sở thích.
  • Có kiến thức căn bản về lập trình ngôn ngữ Javascript

Bắt đầu!!!

Mô hình ứng dụng:

Chia sẻ ảnh NodeJs

Ứng dụng gồm có 3 thành phần:

  • Nodejs server: Ứng dụng vài trò trung gian điều phối .
  • Sender: trang web cho người gởi ảnh
  • Receiver: Trang web cho người nhận ảnh

Ở đây mình làm ra thành 2 trang riêng biệt để dễ phân biệt. Thật ra có thể thiết kế 1 trang vừa gởi vừa nhận cũng được, tùy cách bạn tùy biến.

Chuẩn bị thư mục project của bạn.

Tạo thư mục photoshare, nạp các thư viện cần thiết cho dự án như Socket.io, Expressjs… Chạy các lệnh sau với cmd:

Nếu đường truyền nhà bạn yếu thì có lẽ bạn nên mở nhạc, đứng dậy pha cà phê, sẽ tốn kha khá thời gian đấy. Sau khi tải xong, sẽ có 1 thư mục node_modules  được tạo ra, và cũng chỉ có vậy, không cần để ý đến nó nữa, bắt đầu code thôi!

Lưu ý: Nếu tên thư mục và file ko được nói cụ thể, chỉ có tên thì có nghĩa là nằm trực tiếp trong thư mục gốc của project photoshare/

Dùng Expressjs để tạo web server:

Đầu tiên cần khai báo các gói thư viện cần sử dụng, tạo file server.js và code như dưới:

Ở đây ta dùng Expressjs để có thể tạo server web (đúng hơn là 1 ứng dụng) có khả năng phục vụ các file tĩnh như ảnh, css stylesheet, javascript. Expressjs có thể tự mình khởi chạy ứng dụng, không cần đến gói http, nhưng do chúng ta cũng dùng web socket trên cùng ứng dụng nên cần gói http để kết nối hai thành phần.

Tiếp theo cần cấu hình một chút cho Expressjs.

Thư mục public sẽ là nơi chứa toàn bộ các file tĩnh. Bạn có thể đặt bất kì file nào ở đây và mở thông qua trình duyệt đều được cả. Đây cũng là 1 cách bảo mật ứng dụng, dấu code xử lý ở bên ngoài thư mục public có thể truy cập thông qua http request. Như vậy để tạo 2 trang cho sender/receiver, chỉ cần viết 2 trang html tĩnh đặt vào public là đã truy cập được vào. Mình chọn cách tạo file html tĩnh để đơn giản cho các bạn mới bắt đầu, nếu dùng template engine – jade chẳng hạn – sẽ gây khó khăn cho các bạn mới bắt đầu. Đến khi nào viết ứng dụng cần tạo ra giao diện động, tùy theo từng user thì bạn mới cần template engine.  Bây giờ thì gọi lệnh khởi động server:

Mình dùng port 3000 cho ứng dụng, có thể chọn bất kì port nào khác, nhưng nhớ là nhỏ hơn 65536 (port lớn nhất có thể mở) và và lớn hơn 1000 (dưới 1000 là các port dùng cho các ứng dụng phổ biến khác). Dòng console.log để xuất thông báo lên cửa sổ console chạy ứng dụng, báo cho ta biết ứng dụng đã chạy thành công.

Trước khi chạy thử ứng dụng, nhớ tạo thư mục “photoshare/public“, sau đó thử tạo file public/sender.html:

Để chạy ứng dụng, mở cmd chạy lệnh:

Bây giờ mở Chrome và gõ vào địa chỉ: http://localhost:3000/sender.html. Nếu cả cmd và chrome không báo lỗi gì thì ta đã thành công một nữa rồi đấy.

HTML template cho sender và receiver

Ta sẽ cần 2 trang khác nhau cho 2 người dùng, tạo tempalate đơn giản như sau:

  • Trang gởi: Gồm có nút chọn file để gởi, 1 thẻ img để review xem ảnh được gởi là ảnh nào.
  • Trang nhận: Một thẻ img để sẵn, khi nào có ảnh sẽ chèn vào.

public/sender.html

public/receiver.html

Socket.io – code gởi nhận ảnh phía client

Như trên ta vừa tạo được html template bên ngoài rất đơn giản. Bây giờ cần code thêm 1 xíu cho phía client để kết nối với server và gởi nhận ảnh:

public/sender.script.js

Để mở kết nối đến server, gọi hàm io.connect(), hàm này nhận tham số đầu vào là URL đến server, nếu không có, mặc định nó sẽ connect đến địa chỉ server phục vụ trang hiện tại. Ngay sau khi connect thì client sẽ gởi sự kiện “setRole”  socket.emit("setRole","sender") Xác nhận vai trò của client là sender. Cần handle thêm sự kiện khi người dùng chọn file, thì thực hiện gởi ảnh lên. Trong hàm submitImg(), có 1 đoạn hơi khó hiểu là đoạn tạo FileReader. Do browser không thể truy cập trực tiếp đến file trên ổ cứng, nên cần dùng object này để đọc file và chuyển thành dạng dữ liệu ảnh encode base64. Dữ liệu ảnh base64 này cũng dùng để truyền lên server qua socket.

public/receiver.script.js

Code nhận ảnh chỉ đơn giản là khai báo handle sự kiện nhận ảnh – receivePhoto – khi nào có dữ liệu thì chèn ảnh vào thẻ img có id showPhoto.

Socket.io – Server code : trung gian điều phối

Server sẽ xử lý tất cả 4 sự kiện socket chính, trong đó 2 sự kiện connection , disconnect  là sự kiện built-in của Socket.io, 2 sự kiện sendPhoto  vả receivePhoto  là custom của chính chúng ta. Về ý tưởng của Socket.io, nó sẽ tập trung việc 1 đầu kết nối raise lên sự kiện có tên gì đó, gởi dữ liệu; đầu kết nối còn lại khai báo lắng nghe sự kiện, khi có sự kiện xảy ra thì gọi hàm callback thực hiện. Như vậy luồng xử lý của ứng dụng photoshare chúng ta đang làm như sau:

  1. Sender gởi ảnh qua sự kiện sendPhoto
  2. Server thấy có sender báo “sendPhoto”, liền la lên – “Ê receiver, có người gởi ảnh, mày nhận ảnh nèk!”, server la làng bằng cách gởi sự kiện receiverPhoto cùng dữ liệu (link ảnh)
  3. Receiver đợi sự kiện nhận ảnh nãy giờ, receiver nhận data từ sự kiện receivePhoto, và hiển thị ảnh lên màn hình.

Bạn sẽ cần 1 đoạn code thêm như bên dưới, chèn vào cuối file server.js ta đã có ở trên:

Một điều đặc biệc là hầu hết các sự kiện custom đều phải khai báo và gọi trong hàm callback của sự kiện connection . Điều này đảm bảo các hàm xử lý chính xác chỉ chạy khi đã có kết nối. Hãy làm đúng qui định, còn các qui trình kết nối, tái kết nối, xác thực… đã có Socket.io lo cho bạn. Trong sự kiện sendPhoto  ở trên, mình xử lý lưu ảnh vào thư mục public/upload/ , như vậy dữ liệu ảnh sẽ không phải truyền 2 lần trên đường truyền mạng, do lần gởi đến receiver thực chất chỉ là gởi link ảnh. Nếu bạn không muốn lưu, có thể gởi trực tiếp dữ liệu ảnh data.base64 đến receiver luôn. Socket.io hỗ trợ gởi rất nhiều kiểu dữ liệu: string, object, array, binary,… Bonus thêm 1 tí là hai hàm randomString()  để tạo tên ngẫu nhiên cho file ảnh, đảm bảo ứng dụng của bạn không bị hack bằng chiêu Path Traversal.

Nào, cùng trải nghiệm ma thuật nodejs. Khởi động lại server với lệnh node server.js Kết nối đến 2 trang http://localhost:3000/sender.html và http://localhost:3000/receiver.html trên 2 cửa sổ khác nhau 😀 Cái hay là trang của chúng ta chạy trên mọi thiết bị (dù chả có giao diện gì cả, xấu quắc!) Thử truy cập trên Android, iPhone, Windows Phone … và gởi ảnh tới PC, Mac hay là điện thoại khác, tất cả đều ổn!!! Thật không thể tin được! Chỉ vài dòng code ngắn ngủi đã làm được kha khá chuyện hay ho rồi 😀

Các bạn có thể clone code về từ đây https://github.com/nhtua/photoshare/releases/tag/0.0.1

Phiên bản đầu tiên chỉ có gởi ảnh, phiên bản và bài tutorial kế tiếp mình sẽ thực hiện phần xác thực, làm giao diện với template engine! Nâng cấp từ từ và đón đọc nha!

Update 17/06/2015: Phần 2 – Nâng cấp ứng dụng có khả năng triển khai thực tế, có làm giao diện.

Hoàng Tựa

Yêu thích lập trình web và tạo ra những thứ đẹp đẽ.

10 Comments

  1. chào bạn,mình làm theo hướng dẫn của bạn đã gửi và nhận tốt hình ảnh ở localhost ,nhưng khi gưỉ lên heroku thì không nhận được hình ,xin bạn hướng dẫn

    • @Vinh Mình không có kinh nghiệm triển khai trên Heroku. Song bạn có thể kiểm tra phiên bản NodeJS và SocketIO có tương thích với nhau không nhé. SocketIO hay gặp lỗi khi không tương thích về phiên bản. Ngoài ra có thể kiểm tra xem server Heroku có mở port 3000 hay chưa? Có thể tường lửa đã khóa port trên ứng dụng, liên lạc support để tìm cách mở port. Thân!

      • Mình thử được rồi. Haha, giải pháp cũng thú vị đấy ^^.

      • Mình cũng không thể lưu vào ổ cứng được . Like mạnh

        • Mình chưa làm với Heroku bao giờ. Không biết tại sao lại hay bị vấn đề với ổ cứng? có bạn nào thử write một file test nội dung “abc” gì đấy coi thử Heroku có cho phép tạo file ko?

  2. cho em hỏi em dùng file nodejs vs client là android thì cái emit sendPhoto sẽ truyền cái j vào, đường dẫn file hay là chuỗi base64 của file í

    • nếu server bạn ko sửa code lại thì android vẫn phải convert ảnh sang base64. Bản này mình làm demo, ảnh gởi bằng base64 sẽ nặng hơn, chậm hơn. Nên gởi dạng binary file luôn cho nhẹ.

Leave a Reply

Your email address will not be published. Required fields are marked *