成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

使用 Docker 和 Node 快速實(shí)現(xiàn)一個(gè)在線的 QRCode 解碼服務(wù)

fengxiuping / 986人閱讀

摘要:本文使用署名國(guó)際許可協(xié)議,歡迎轉(zhuǎn)載或重新修改使用,但需要注明來(lái)源。署名國(guó)際本文作者蘇洋創(chuàng)建時(shí)間年月日統(tǒng)計(jì)字?jǐn)?shù)字閱讀時(shí)間分鐘閱讀本文鏈接使用和快速實(shí)現(xiàn)一個(gè)在線的解碼服務(wù)本文將會(huì)介紹如何使用完成一個(gè)簡(jiǎn)單的二維碼解析服務(wù),全部代碼在行以內(nèi)。

本文使用「署名 4.0 國(guó)際 (CC BY 4.0)」許可協(xié)議,歡迎轉(zhuǎn)載、或重新修改使用,但需要注明來(lái)源。 署名 4.0 國(guó)際 (CC BY 4.0)

本文作者: 蘇洋

創(chuàng)建時(shí)間: 2018年12月09日
統(tǒng)計(jì)字?jǐn)?shù): 5453字
閱讀時(shí)間: 11分鐘閱讀
本文鏈接: https://soulteary.com/2018/12...


使用 Docker 和 Node 快速實(shí)現(xiàn)一個(gè)在線的 QRCode 解碼服務(wù)

本文將會(huì)介紹如何使用 Docker、Node、JavaScript、Traefik完成一個(gè)簡(jiǎn)單的二維碼解析服務(wù),全部代碼在 300 行以內(nèi)。

最近折騰文章相關(guān)的東西比較多,其中有一個(gè)現(xiàn)代化要素其實(shí)挺麻煩的,就是二維碼。

不論是“生成動(dòng)態(tài)、靜態(tài)的二維碼”,還是“對(duì)已經(jīng)生成的二維碼進(jìn)行解析”,其實(shí)都不難實(shí)現(xiàn)。只是在日常工作中如果只是基于命令行去操作,會(huì)很不方便。

所以花了點(diǎn)時(shí)間,實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的 QRCode 在線解析工具,在完成這個(gè)工具之后,原本需要“打開(kāi)終端,定位文件,執(zhí)行命令,等待結(jié)果”就簡(jiǎn)化成了“打開(kāi)網(wǎng)頁(yè),CTRL+V 粘貼,片刻展示結(jié)果”,當(dāng)然,因?yàn)轭~外提供了接口,所以也可以當(dāng)一個(gè)無(wú)狀態(tài)服務(wù)使用。

實(shí)現(xiàn)服務(wù)端核心解析邏輯

核心邏輯其實(shí)很簡(jiǎn)單,偽代碼三行就差不多了,比如。

const uploadContentByUser = req.body.files;
const decodeContent = decodeImage(uploadContentByUser);
const result = decodeQR.load(decodeContent);

但是實(shí)際使用的情況,出于性能的考慮,我不會(huì)過(guò)分使用新語(yǔ)法進(jìn)行代碼封裝,更傾向盡可能使用“原生”的回調(diào)模式進(jìn)行異步編程,避免各種“wrapper”造成不必要的損耗。

因?yàn)樽罱K的目的是“在瀏覽器里一個(gè)粘貼/拖拽操作就完事”。所以我們需要將上面的核心邏輯展開(kāi),根據(jù)“簡(jiǎn)單項(xiàng)目不過(guò)度封裝”的思想,代碼會(huì)膨脹為下面三十行左右的樣子。

app.post("/api/decode", multipartMiddleware, function(req, res) {
  let filePath = "";

  try {
    if (req.files.imageFile.path) filePath = req.files.imageFile.path;
  } catch (e) {
    return res.json({code: 500, content: "request params error."});
  }

  fs.readFile(filePath, function(errorWhenReadUploadFile, fileBuffer) {
    if (errorWhenReadUploadFile) return res.json({code: 501, content: "read upload file error."});
    decodeImage(fileBuffer, function(errorWhenDecodeImage, image) {
      if (errorWhenDecodeImage) return res.json({code: 502, content: errorWhenDecodeImage});
      let decodeQR = new qrcodeReader();
      decodeQR.callback = function(errorWhenDecodeQR, result) {
        if (errorWhenDecodeQR) return res.json({code: 503, content: errorWhenDecodeQR});
        if (!result) return res.json({code: 404, content: "gone with wind"});
        return res.json({code: 200, content: result.result, points: result.points});
      };
      decodeQR.decode(image.bitmap);
    });
  });
});

上面的邏輯很簡(jiǎn)單,主要做了下面幾件事:

接受用戶上傳的文件

讀取用戶上傳的文件

解析用戶上傳的文件

嘗試將文件中的信息解碼并反饋用戶

其中依賴了一個(gè) express 三方的中間件 multipartMiddleware,我將主要使用它來(lái)進(jìn)行上傳文件的請(qǐng)求序列化,源碼十分簡(jiǎn)潔,一百行左右,有興趣可以去瀏覽一下。

它的使用也十分簡(jiǎn)單,無(wú)需配置,只需要兩行就能發(fā)揮作用。

const multipart = require("connect-multiparty");
const multipartMiddleware = multipart();

當(dāng)然,為了能夠配合客戶端 JavaScript 完成我們的最終目標(biāo),我們需要一些額外的代碼,比如:提供一個(gè)瀏覽器可以瀏覽的頁(yè)面。

這里額外提一點(diǎn),如果使用類(lèi) express 的框架,一般會(huì)有一個(gè) static 方法,讓你設(shè)置一個(gè)靜態(tài)文件目錄,可以免編程路由邏輯對(duì)一些文件進(jìn)行對(duì)外訪問(wèn),比如這樣:

app.use(express.static(__dirname + "/static", {dotfiles: "ignore", etag: false, extensions: ["html"], index: false, maxAge: "1h", redirect: false}));

但是,本例中我其實(shí)只需要一個(gè)入口頁(yè)面就能滿足需求,根本不需要外部資源,比如 vue、react、jq、各種css框架

這個(gè)時(shí)候,我推薦直接將要展示的頁(yè)面使用 fs API 進(jìn)行內(nèi)存緩存,直接提供用戶即可,比如按照下面的代碼進(jìn)行編寫(xiě),大概十行就能滿足需求。

const indexCache = fs.readFileSync("./index.html");

app.get("/", function(req, res) {
  res.redirect("/index.html");
});

app.get("/index.html", function(req, res) {
  res.setHeader("charset", "utf-8");
  res.setHeader("Content-Type", "text/html");
  res.send(indexCache);
});

當(dāng)然,如果你想要和 static 方式的文件一樣,在調(diào)試過(guò)程中,可以“熱更新”文件的話,需要將這個(gè) indexCache 改寫(xiě)成一個(gè)方法,在攔截用戶請(qǐng)求之后,每次都去動(dòng)態(tài)讀取文件,或者更高階一些,根據(jù)文件最后編輯時(shí)間戳,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 LRU 緩存。

實(shí)現(xiàn)客戶端交互邏輯

在實(shí)現(xiàn)完畢接口后,我們把欠缺的前端交互邏輯補(bǔ)全。

這里因?yàn)闆](méi)有什么重度的操作,界面也很簡(jiǎn)單,所以既不需要 jQ 這類(lèi)庫(kù),也不需要 Vue、React 這類(lèi)框架,直接寫(xiě)腳本就是了。

腦補(bǔ)我需要的界面,上面是一個(gè)數(shù)據(jù)交互的區(qū)域,下面是我的交互結(jié)果列表,因?yàn)轫?yè)面也沒(méi)幾個(gè)元素,所以直接使用腳本進(jìn)行元素的創(chuàng)建和操作吧。

let uploadBox = document.createElement("textarea");
uploadBox.id = "upload";
uploadBox.placeholder = "Paste Here.";
document.body.appendChild(uploadBox);

let list = document.createElement("ul");
list.id = "result";
document.body.appendChild(list);

瀏覽器端核心的操作有三個(gè):

接受用戶的拖拽和粘貼圖片的操作

將用戶給予的圖片數(shù)據(jù)進(jìn)行上傳

對(duì)服務(wù)端接口解析的結(jié)果進(jìn)行展示

我們先來(lái)實(shí)現(xiàn)第一個(gè)操作,拖拽、粘貼富交互功能,大概三十行代碼就能解決戰(zhàn)斗。

function getFirstImage(data, isDrop) {
  let i = 0, item;
  let target = isDrop ?
      data.dataTransfer && data.dataTransfer.files :
      data.clipboardData && data.clipboardData.items;

  if (!target) return false;

  while (i < target.length) {
    item = target[i];
    if (item.type.indexOf("image") !== -1) return item;
    i++;
  }
  return false;
}

function getFilename(event) {
  return event.clipboardData.getData("text/plain").split("
")[0];
}

uploadBox.addEventListener("paste", function(event) {
  event.preventDefault();
  const image = getFirstImage(event);
  if (image) return uploadFile(image.getAsFile(), getFilename(event) || "image.png");
});

uploadBox.addEventListener("drop", function(event) {
  event.preventDefault();
  const image = getFirstImage(event, true);
  if (image) return uploadFile(image, event.dataTransfer.files[0].name || "image.png");
});

如果你需要支持多張圖片上傳,服務(wù)端接口需要做一個(gè)簡(jiǎn)單的改動(dòng),我沒(méi)有這個(gè)需求,就不做了,有興趣可以實(shí)踐下,理論上加兩個(gè)循環(huán)就完事。

接著我們繼續(xù)實(shí)現(xiàn)上傳功能,因?yàn)楝F(xiàn)代的瀏覽器都支持了 fetch,所以實(shí)現(xiàn)起來(lái)也很簡(jiǎn)單,二十多行解決戰(zhàn)斗:

function getMimeType(file, filename) {
  if (!file) return console.warn("不支持該文件類(lèi)型");
  const mimeType = file.type;
  const extendName = filename.substring(filename.lastIndexOf(".") + 1);
  if (mimeType !== "image/" + extendName) return "image/" + extendName;
  return mimeType;
}

function uploadFile(file, filename) {
  let formData = new FormData();
  formData.append("imageFile", file);

  let fileType = getMimeType(file, filename);
  if (!fileType || ["jpg", "jpeg", "gif", "png", "bmp"].indexOf(fileType) > -1) return console.warn("文件格式不正確");

  formData.append("mimeType", fileType);

  fetch("/api/decode", {method: "POST", body: formData}).
      then((response) => response.json()).
      then((data) => {
        if (data.code === 200) return addResult(filename, data.content);
        return addResult(filename, data.content);
      }).
      catch((error) => addResult(filename, error));
}

最后,寫(xiě)幾條樣式規(guī)則,額外優(yōu)化一下解析結(jié)果展示就完事了,比如能夠更輕松的復(fù)制解析結(jié)果。

list.addEventListener("mouseover", function(e) {
  let target = e.target;
  if (target && target.nodeName) {
    if (target.nodeName.toLowerCase() === "input") {
      target.select();
    }
  }
});

function result(file, text) {
  let li = document.createElement("li");
  li.innerHTML = "" + file + "" + "";
  document.getElementById("result").appendChild(li);
}
將程序容器化

如果你認(rèn)真閱讀了上面的文章,你會(huì)發(fā)現(xiàn),實(shí)際的程序只有兩個(gè)文件,一個(gè)是服務(wù)端的 Node 程序,另外一個(gè)則是我們的客戶端頁(yè)面,但是實(shí)際上,我們還需要一個(gè)記錄 Node 依賴的 package.json 以及一個(gè)用戶構(gòu)建容器鏡像的 Dockerfile,最簡(jiǎn)化的目錄結(jié)構(gòu)如下:

.
├── Dockerfile
├── index.html
├── index.js
└── package.json

考慮實(shí)際維護(hù),我們還需要額外創(chuàng)建一些其他的問(wèn)題,不過(guò)都不重要,相關(guān)的文件內(nèi)容,可以瀏覽我稍后提供的源碼倉(cāng)庫(kù)。

此刻,當(dāng)我們執(zhí)行 node index.js,然后在瀏覽器中打開(kāi) localhost:3000 就能實(shí)現(xiàn)文章一開(kāi)頭我們提到的一鍵粘貼完成對(duì)二維碼的解析操作了。

不過(guò)為了部署的便捷,我們還是需要將程序進(jìn)行容器化操作。我們來(lái)著重瀏覽一下容器構(gòu)建文件,同樣很簡(jiǎn)單,幾行就足夠我們的使用。

FROM node:11.4.0-alpine
MAINTAINER soulteary 

RUN apk update && apk add yarn
WORKDIR /app
COPY .  /app
RUN yarn

ENTRYPOINT [ "node", "index.js" ]

配合簡(jiǎn)單的構(gòu)建命令:

docker build -t "docker.soulteary.com/decode-qrcode.soulteary.com:0.0.1" .

稍等一兩分鐘,就能夠獲得一個(gè)可以脫離當(dāng)前環(huán)境,隨處運(yùn)行的容器鏡像了。如果你想讓容器運(yùn)行起來(lái),也只需要一條命令,即可。

docker run -it -p 3000:3000 "docker.soulteary.com/decode-qrcode.soulteary.com:0.0.1"

如果每次都使用這樣的命令,未免麻煩,我們不妨使用 compose 配合 Traefik 進(jìn)行服務(wù)化。

配合 Traefik 進(jìn)行服務(wù)化操作

配合 compose 和 Traefik 使用起來(lái)非常簡(jiǎn)單,我之前的文章有提過(guò)多次,所以這里就簡(jiǎn)單貼出配置文件示例:

version: "3"

services:

  decode:
    image: docker.soulteary.com/decode-qrcode.soulteary.com:0.0.1
    expose:
      - 3000
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.port=3000"
      - "traefik.frontend.rule=Host:decode-qrcode.lab.com"
      - "traefik.frontend.entryPoints=http,https"

networks:
  traefik:
    external: true

然后使用 docker-compose -f compose.yml up -d 即可自動(dòng)啟動(dòng)服務(wù),并將服務(wù)自動(dòng)注冊(cè)到 Traefik 的服務(wù)發(fā)現(xiàn)上。

如果需要擴(kuò)容,scale decode=4 即可,如果還不會(huì)操作,可以翻閱之前的文章,進(jìn)一步學(xué)習(xí),: )

最后

附上完整示例代碼: https://github.com/soulteary/decode-your-qrcode

最近結(jié)束了休假,換了新公司,手頭事情比較多,寫(xiě)文章的速度會(huì)慢一些,不過(guò)沒(méi)有關(guān)系,草稿箱里的東西積累的再多一些,文章的質(zhì)量會(huì)再上一層樓,一起期待一下吧。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/27637.html

相關(guān)文章

  • node打造微信個(gè)人號(hào)機(jī)器人

    摘要:是一款開(kāi)源的微信個(gè)人號(hào),進(jìn)行了一系列的封裝,提供簡(jiǎn)單好用的接口,然后開(kāi)發(fā)者可以在其之上進(jìn)行微信機(jī)器人的開(kāi)發(fā)。注意這行代碼實(shí)現(xiàn)了登錄微信個(gè)人號(hào)并打印出所收到的消息。大家可以根據(jù)自己的需要定制出強(qiáng)大的個(gè)人微信號(hào)機(jī)器人。 現(xiàn)在,日常生活已經(jīng)離不開(kāi)微信,本文將會(huì)拋磚引玉演示如何使用wechaty操作微信個(gè)人號(hào)做一些有意思的東西,可以實(shí)現(xiàn)自動(dòng)通過(guò)好友請(qǐng)求、關(guān)鍵詞回復(fù)、自動(dòng)拉群等功能。大大提高了社...

    xiaolinbang 評(píng)論0 收藏0
  • 前端常用插件、工具類(lèi)庫(kù)匯總

    摘要:頁(yè)面調(diào)試騰訊開(kāi)發(fā)維護(hù)的代碼調(diào)試發(fā)布,錯(cuò)誤監(jiān)控上報(bào),用戶問(wèn)題定位。同樣是由騰訊開(kāi)發(fā)維護(hù)的代碼調(diào)試工具,是針對(duì)移動(dòng)端的調(diào)試工具。前端業(yè)務(wù)代碼工具庫(kù)。動(dòng)畫(huà)庫(kù)動(dòng)畫(huà)庫(kù),也是目前通用的動(dòng)畫(huà)庫(kù)。 本人微信公眾號(hào):前端修煉之路,歡迎關(guān)注 本篇文章整理自己使用過(guò)的和看到過(guò)的一些插件和工具,方便日后自己查找和使用。 另外,感謝白小明,文中很多的工具來(lái)源于此。 彈出框 layer:http://layer....

    GitCafe 評(píng)論0 收藏0
  • PHP生成微信小程序二維碼,可生成帶參數(shù)二維碼。

    摘要:微信小程序官方開(kāi)放了個(gè)創(chuàng)建二維碼的接口,其中有一個(gè)是生成二維碼的,還有一個(gè)是葵花狀的小程序碼,我這里就用生成二維碼。 微信小程序官方開(kāi)放了3個(gè)創(chuàng)建二維碼的接口,其中有一個(gè)是生成二維碼的,還有一個(gè)是葵花狀的小程序碼,我這里就用php生成二維碼。 首先要獲取Access_token 這個(gè)請(qǐng)求起來(lái)也是很容易的,微信開(kāi)發(fā)文檔有請(qǐng)求接口:要把自己的小程序的APPID和APPSECRET獲取到 h...

    jsliang 評(píng)論0 收藏0
  • PHP生成微信小程序二維碼,可生成帶參數(shù)二維碼。

    摘要:微信小程序官方開(kāi)放了個(gè)創(chuàng)建二維碼的接口,其中有一個(gè)是生成二維碼的,還有一個(gè)是葵花狀的小程序碼,我這里就用生成二維碼。 微信小程序官方開(kāi)放了3個(gè)創(chuàng)建二維碼的接口,其中有一個(gè)是生成二維碼的,還有一個(gè)是葵花狀的小程序碼,我這里就用php生成二維碼。 首先要獲取Access_token 這個(gè)請(qǐng)求起來(lái)也是很容易的,微信開(kāi)發(fā)文檔有請(qǐng)求接口:要把自己的小程序的APPID和APPSECRET獲取到 h...

    afishhhhh 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<