欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Node配合WebSocket做多文件下載以及進(jìn)度回傳

 更新時間:2019年11月07日 13:30:50   作者:糊糊糊糊糊了  
這篇文章主要介紹了Node配合WebSocket做多文件下載以及進(jìn)度回傳功能,本文通過實(shí)例代碼效果截圖給大家介紹的非常詳細(xì),需要的朋友可以參考下

起因

為什么做這個東西,是突然間聽一后端同事說起Annie這個東西,發(fā)現(xiàn)這個東西下載視頻挺方便的,會自動爬取網(wǎng)頁中的視頻,然后整理成列表。發(fā)現(xiàn)用命令執(zhí)行之后是下面的樣子:

心里琢磨了下,整一個界面玩一下吧。然后就做成下面這個樣子了。

列表

下載列表

本文地址倉庫:https://github.com/Rynxiao/yh-tools,如果喜歡,歡迎star.

涉及技術(shù)

  • Express 后端服務(wù)
  • Webpack 模塊化編譯工具
  • Nginx 主要做文件gzip壓縮(發(fā)現(xiàn)Express添加gzip有點(diǎn)問題,才棄坑nginx)
  • Ant-design 前端UI庫
  • React + React Router
  • WebSocket 進(jìn)度回傳服務(wù)

其中還有點(diǎn)小插曲,最開始是使用docker起了一個nginx服務(wù),但是發(fā)現(xiàn)內(nèi)部轉(zhuǎn)發(fā)一直有問題,同時獲取宿主主機(jī)IP也出現(xiàn)了點(diǎn)問題,然后折磨了好久放棄了。(docker研究不深,敬請諒解^_^)

下載部分細(xì)節(jié)

首先瀏覽器會連接WebSocket服務(wù)器,同時在WebSocket服務(wù)器上存在一個所有客戶端的Map,瀏覽器端生成一個uuid作為瀏覽器客戶端id,然后將這個鏈接作為值存進(jìn)Map中。

客戶端:

// list.jsx
await WebSocketClient.connect((event) => {
 const data = JSON.parse(event.data);
 if (data.event === 'close') {
  this.updateCloseStatusOfProgressBar(list, data);
 } else {
  this.generateProgressBarList(list, data);
 }
});
// src/utils/websocket.client.js
async connect(onmessage, onerror) {
 const socket = this.getSocket();
 return new Promise((resolve) => {
  // ...
 });
}
getSocket() {
 if (!this.socket) {
  this.socket = new WebSocket(
   `ws://localhost:${CONFIG.PORT}?from=client&id=${clientId}`,
   'echo-protocol',
  );
 }
 return this.socket;
}

服務(wù)端:

// public/javascript/websocket/websocket.server.js
connectToServer(httpServer) {
 initWsServer(httpServer);
 wsServer.on('request', (request) => {
  // uri: ws://localhost:8888?from=client&id=xxxx-xxxx-xxxx-xxxx
  logger.info('[ws server] request');
  const connection = request.accept('echo-protocol', request.origin);
  const queryStrings = querystring.parse(request.resource.replace(/(^\/|\?)/g, ''));
  
  // 每有連接連到websocket服務(wù)器,就將當(dāng)前連接保存到map中
  setConnectionToMap(connection, queryStrings);
  connection.on('message', onMessage);
  connection.on('close', (reasonCode, description) => {
   logger.info(`[ws server] connection closed ${reasonCode} ${description}`);
  });
 });

 wsServer.on('close', (connection, reason, description) => {
  logger.info('[ws server] some connection disconnect.');
  logger.info(reason, description);
 });
}

然后在瀏覽器端點(diǎn)擊下載的時候,會傳遞兩個主要的字段resourceId(在代碼中由parentId和childId組成)和客戶端生成的bClientId。這兩個id有什么用呢?

每次點(diǎn)擊下載,都會在Web服務(wù)器中生成一個WebSocket的客戶端,那么這個resouceId就是作為在服務(wù)器中生成的WebSocket服務(wù)器的key值。

bClientId主要是為了區(qū)分瀏覽器的客戶端,因?yàn)榭紤]到同時可能會有多個瀏覽器接入,這樣在WebSocket服務(wù)器中產(chǎn)生消息的時候,就可以用這個id來區(qū)分應(yīng)該發(fā)送給哪個瀏覽器客戶端

客戶端:

// list.jsx
http.get(
 'download',
 {
  code,
  filename,
  parent_id: row.id,
  child_id: childId,
  download_url: url,
  client_id: clientId,
 },
);
// routes/api.js
router.get('/download', async (req, res) => {
 const { code, filename } = req.query;
 const url = req.query.download_url;
 const clientId = req.query.client_id;
 const parentId = req.query.parent_id;
 const childId = req.query.child_id;
 const connectionId = `${parentId}-${childId}`;
 const params = {
  code,
  url,
  filename,
  parent_id: parentId,
  child_id: childId,
  client_id: clientId,
 };
 const flag = await AnnieDownloader.download(connectionId, params);
 if (flag) {
  await res.json({ code: 200 });
 } else {
  await res.json({ code: 500, msg: 'download error' });
 }
});
// public/javascript/annie.js
async download(connectionId, params) {
  //...
 // 當(dāng)annie下載時,會進(jìn)行數(shù)據(jù)監(jiān)聽,這里會用到節(jié)流,防止進(jìn)度回傳太快,websocket服務(wù)器無法反應(yīng)
 downloadProcess.stdout.on('data', throttle((chunk) => {
  try {
   if (!chunk) {
    isDownloading = false;
   }
   // 這里主要做的是解析數(shù)據(jù),然后發(fā)送進(jìn)度和速度等信息給websocket服務(wù)器
   getDownloadInfo(chunk, ws, params);
  } catch (e) {
   downloadSuccess = false;
   WsClient.close(params.client_id, connectionId, 'download error');
   this.stop(connectionId);
   logger.error(`[server annie download] error: ${e}`);
  }
 }, 500, 300));
}

服務(wù)端收到進(jìn)度以及速度的消息后,回傳給客戶端,如果進(jìn)度達(dá)到了100%,那么就刪除掉存在server中的服務(wù)器中起的websocket的客戶端,并且發(fā)送一個客戶端被關(guān)閉的通知,通知瀏覽器已經(jīng)下載完成。

// public/javascript/websocket/websocket.server.js
function onMessage(message) {
 const data = JSON.parse(message.utf8Data);
 const id = data.client_id;
 if (data.event === 'close') {
  logger.info('[ws server] close event');
  closeConnection(id, data);
 } else {
  getConnectionAndSendProgressToClient(data, id);
 }
}
function getConnectionAndSendProgressToClient(data, clientId) {
 const browserClient = clientsMap.get(clientId);
 // logger.info(`[ws server] send ${JSON.stringify(data)} to client ${clientId}`);
 if (browserClient) {
  const serverClientId = `${data.parent_id}-${data.child_id}`;
  const serverClient = clientsMap.get(serverClientId);
  // 發(fā)送從web服務(wù)器中傳過來的進(jìn)度、速度給瀏覽器
  browserClient.send(JSON.stringify(data));
  // 如果進(jìn)度已經(jīng)達(dá)到了100%
  if (data.progress >= 100) {
   logger.info(`[ws server] file has been download successfully, progress is ${data.progress}`);
   logger.info(`[ws server] server client ${serverClientId} ready to disconnect`);
   // 從clientsMap將當(dāng)前的這個由web服務(wù)器創(chuàng)建的websocket客戶端移除
   // 然后關(guān)閉當(dāng)前連接
   // 同時發(fā)送下載完成的消息給瀏覽器
   clientsMap.delete(serverClientId);
   serverClient.send(JSON.stringify({ connectionId: serverClientId, event: 'complete' }));
   serverClient.close('download completed');
  }
 }
}

整體來說就這么多,有一點(diǎn)需要指出,annie在解析的時候有時候可能消息處理不是很穩(wěn)定,導(dǎo)致我數(shù)據(jù)解析的時候出現(xiàn)了一些問題,但是我用mock的數(shù)據(jù)以及mock的進(jìn)度條回傳是不會出現(xiàn)問題的。

相關(guān)文章

  • Node.js websocket使用socket.io庫實(shí)現(xiàn)實(shí)時聊天室

    Node.js websocket使用socket.io庫實(shí)現(xiàn)實(shí)時聊天室

    這篇文章主要為大家詳細(xì)介紹了Node.js websocket使用socket.io庫實(shí)現(xiàn)實(shí)時聊天室,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • NodeJS使用JWT跨域身份驗(yàn)證方案詳解

    NodeJS使用JWT跨域身份驗(yàn)證方案詳解

    JWT是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標(biāo)準(zhǔn),其組成部分為Header、Payload、Signature.Payload部分才是真正的用戶信息,它是用戶信息經(jīng)過加密之后生成的字符串,Header和Signature是安全性相關(guān)的部分,只是為了保證token的安全性
    2023-02-02
  • Node.js中Bootstrap-table的兩種分頁的實(shí)現(xiàn)方法

    Node.js中Bootstrap-table的兩種分頁的實(shí)現(xiàn)方法

    這篇文章主要介紹了Node.js中Bootstrap-table的兩種分頁的使用方法,需要的朋友可以參考下
    2017-09-09
  • 深入淺析Node.js 事件循環(huán)、定時器和process.nextTick()

    深入淺析Node.js 事件循環(huán)、定時器和process.nextTick()

    這篇文章主要介紹了Node.js 事件循環(huán)、定時器和process.nextTick()的相關(guān)知識,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-10-10
  • Nodejs 微信小程序消息推送的實(shí)現(xiàn)

    Nodejs 微信小程序消息推送的實(shí)現(xiàn)

    這篇文章主要介紹了Nodejs 微信小程序消息推送的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Node.js流管理利器Destroy包使用方法詳解

    Node.js流管理利器Destroy包使用方法詳解

    在 Node.js 中,流(Streams)是處理大量數(shù)據(jù)的高效方式,然而,流的不當(dāng)處理可能會引發(fā)資源泄露,本文將詳解如何使用 destroy 包,確保流被正確銷毀,需要的朋友可以參考下
    2024-05-05
  • 使用PM2實(shí)現(xiàn)高效的應(yīng)用監(jiān)控與管理功能

    使用PM2實(shí)現(xiàn)高效的應(yīng)用監(jiān)控與管理功能

    PM2?是一個流行的進(jìn)程管理器,用于?Node.js?應(yīng)用程序,它支持應(yīng)用程序的負(fù)載均衡、自動重啟、日志管理、監(jiān)控以及多環(huán)境管理等功能,本文給大家介紹了如何使用PM2實(shí)現(xiàn)高效的應(yīng)用監(jiān)控與管理功能,需要的朋友可以參考下
    2024-02-02
  • NodeJS配置HTTPS服務(wù)實(shí)例分享

    NodeJS配置HTTPS服務(wù)實(shí)例分享

    本文給大家分享的是在nodejs中配置https服務(wù)的方法和具體的示例,非常的詳細(xì),有需要的小伙伴可以來參考下
    2017-02-02
  • node.js中的http.request方法使用說明

    node.js中的http.request方法使用說明

    這篇文章主要介紹了node.js中的http.request方法使用說明,本文介紹了http.request的方法說明、語法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼需要的朋友可以參考下
    2014-12-12
  • Node升級后vue項(xiàng)目node-sass報錯問題及解決

    Node升級后vue項(xiàng)目node-sass報錯問題及解決

    這篇文章主要介紹了Node升級后vue項(xiàng)目node-sass報錯問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03

最新評論