关于node.js:Nodejs安装及环境配置

一、装置环境1、本机系统:Windows 10 专业版(64位) 2、Node.js:node-v8.9.4-x64.msi(64位) 二、装置Node.js步骤1、下载对应本人零碎对应的 Node.js 版本,地址:https://nodejs.org/zh-cn/ 2、选装置目录进行装置 3、环境配置 4、测试 三、后期筹备1、Node.js简介 Node.js® 是一个基于 Chrome V8 引擎的 JavaScript 运行时。 Node.js 应用高效、轻量级的事件驱动、非阻塞 I/O 模型。它的包生态系统,npm,是目前世界上最大的开源库生态系统。 2、下载Node.js 官网地址:https://nodejs.org/en/ 或 https://nodejs.org/zh-cn/ 我下载的是 node-v8.9.4-x64.msi ,如下图: 四、开始装置 1、下载实现后,双击“ node-v8.9.4-x64.msi ”,开始装置: 2、点击“ Next ”按钮 3、抉择装置目录,点击“ Next ”按钮 4、抉择装置项,此处我抉择默认,点击“ Next ”按钮 5、点击“ Install ”按钮,开始装置 6、期待装置实现,点击“ Finish ”按钮实现装置 五、装置实现查看 1、查看是否装置胜利 A、node -v 查看 node 版本 B、npm -v 查看 npm 版本 2、装置实现后,文件目录如下图 六、环境配置 此处的环境配置次要配置的是 npm 装置的全局模块所在的门路,以及缓存cache的门路,之所以要配置,是因为当前在执行相似:npm install express [-g] (前面的可选参数-g,g代表global全局装置的意思)的装置语句时,会将装置的模块装置到【C:\Users\用户名\AppData\Roaming\npm】门路中,占C盘空间。 例如:我心愿将全模块所在门路和缓存门路放在我node.js装置的文件夹中,则在我的装置目录下创立两个文件夹【node_global】及【node_cache】如下图: 1、设置全局目录和缓存目录,创立完两个空文件夹之后,关上cmd命令窗口,输出 ...

July 27, 2020 · 1 min · jiezi

关于node.js:从Deno跟Node的性能对比说起

往年五月份Deno公布了1.0版本,作为一个常常用Node来构建我的项目的前端,对Deno官网形容的那几点长处其实并不太关怀(Deno长处)。次要还是想晓得Deno的性能怎么样,用Deno能不能大幅缩小前端构建我的项目的耗时。对网络上Deno能不能代替Node的探讨也比拟感兴趣,于是便用Deno跟Node去执行一些罕用的办法,比拟它们的性能,钻研下Deno是否能够代替Node。 Deno简介Deno是一个JavaScript和TypeScript运行时,跟Node、Java一样能够运行在服务器上。 测试信息Node版本v12.18.0,Deno版本1.1.0。执行后果是执行了5次雷同办法取的范畴。第三方库用了spark-md5、js-base64、acorn、estraverse和escodegen。 用sort排序数组(数组长度为20万)console.time("sort-array");dataArray.sort((a, b) => { return a - b;});console.timeEnd("sort-array");node:84.645ms-103.086msdeno:134ms-140ms md5(文件大小为2.6M)console.time("md5");spark.append(data);console.log(`md5: ${spark.end()}`);console.timeEnd("md5");node 46.930ms-71.454ms deno 63ms-83ms base64(文件大小为2.6M)console.time("Base64");Base64.encode(data);console.timeEnd("Base64");node 557.819ms-688.570ms deno 1216ms-1269ms 斐波那契数列(n=40)function fib(n) { if (n === 0) { return 0; } else if (n === 1) { return 1; } else { return fib(n - 1) + fib(n - 2); }}console.time("fib");fib(40);console.timeEnd("fib");node 1301.254ms-1387.108msdeno 1447ms-1616ms for in遍历对象(10万个属性)console.time("for in");for (let k in dataObject) { total += dataObject[k].list[0].n;}console.timeEnd("for in");node 39.923ms-56.622msdeno 41ms-49ms ...

July 25, 2020 · 1 min · jiezi

关于node.js:Node-中如何引入一个模块及其细节

本文收录于 GitHub 山月行博客: shfshanyue/blog,内含我在理论工作中碰到的问题、对于业务的思考及在全栈方向上的学习 前端工程化系列Node进阶系列在 node 环境中,有两个内置的全局变量无需引入即可间接应用,并且无处不见,它们形成了 nodejs 的模块体系: module 与 require。以下是一个简略的示例 const fs = require('fs')const add = (x, y) => x + ymodule.exports = add尽管它们在平时应用中仅仅是引入与导出模块,但稍稍深刻,便可见乾坤之大。在业界可用它们做一些比拟 trick 的事件,尽管我不大倡议应用这些黑科技,但略微理解还是很有必要。 如何在不重启利用时热加载模块?如 require 一个 json 文件时会产生缓存,然而重写文件时如何 watch如何通过不侵入代码进行打印日志循环援用会产生什么问题?module wrapper当咱们应用 node 中写一个模块时,实际上该模块被一个函数包裹,如下所示: (function(exports, require, module, __filename, __dirname) { // 所有的模块代码都被包裹在这个函数中 const fs = require('fs') const add = (x, y) => x + y module.exports = add});因而在一个模块中主动会注入以下变量: exportsrequiremodule__filename__dirname module调试最好的方法就是打印,咱们想晓得 module 是何方神圣,那就把它打印进去! const fs = require('fs')const add = (x, y) => x + ymodule.exports = addconsole.log(module) ...

July 22, 2020 · 3 min · jiezi

关于node.js:npm更换国内源解决npm-install慢的问题

1.问题还原在国内应用 npm装置的时候,会十分迟缓, npm install 倡议应用淘宝的镜像仓库来代替的原有的镜像仓库(镜像仓库就是一个npm的下载源,换成国内的下载源,下载速度会晋升很多) 2.解决方案侥幸的是,国内有几个镜像站点能够供咱们应用,速度十分快,镜像站会实时更新,为咱们节俭了好多工夫.如何给本机换源呢?(1)[长期]通过 config 配置指向国内镜像源 # 配置指向源$ npm config set registry http://registry.npm.taobao.org(2)[长期]通过 npm 命令指定下载源 # 在装置时候长期指定$ npm --registry http://registry.cnpmjs.org info express(3)长久应用 $ npm config set registry https://registry.npm.taobao.org(4)永恒批改镜像源 查看 npm 配置$ npm config list# 其余查看配置的形式$ npm config get globalconfig$ npm config ls -l 找到并关上配置文件:~/.npmrc写入配置:registry=https://registry.npm.taobao.org3.验证设置是否胜利npm config get registry# ORnpm info express4.重要揭示不举荐通过cnpm 应用,会呈现各种莫名的问题。5.其余镜像开源镜像: http://npm.taobao.org/mirrorsNode.js 镜像: http://npm.taobao.org/mirrors/nodealinode 镜像: http://npm.taobao.org/mirrors/alinodephantomjs 镜像: http://npm.taobao.org/mirrors/phantomjsChromeDriver 镜像: http://npm.taobao.org/mirrors/chromedriverOperaDriver 镜像: http://npm.taobao.org/mirrors/operadriverSelenium 镜像: http://npm.taobao.org/mirrors/seleniumNode.js 文档镜像: http://npm.taobao.org/mirrors/node/latest/docs/api/index.htmlNPM 镜像: https://npm.taobao.org/mirrors/npm/electron 镜像: https://npm.taobao.org/mirrors/electron/node-inspector 镜像: https://npm.taobao.org/mirrors/node-inspector/

July 21, 2020 · 1 min · jiezi

关于node.js:nodejs-windows-安装-jsdom

刚做逆向须要装置jsdom模块,部署在服务器的时候嫩死canvas@^2.5.0 装不上,又是下载 C++ 环境,又是换32位包,等等3天辛酸泪. 前面看了下本人电脑的 node 安装包 node-v.12.16.1-×64 ,又去下载了这个版本尝试. 发表问题解决. node-v.12.16.1-×64 下载地址

July 17, 2020 · 1 min · jiezi

gyp-ERR-find-Python-解决方案

命令行报错如下E:\vue-admin\node_modules\fibers>if not defined npm_config_node_gyp (node "D:\nodejs\node_modules\npm\node_modules\npm-lifecycle\node-gyp-bin\\..\..\node_modules\node-gyp\bin\node-gyp.js" rebuild --release ) else (node "D:\nodejs\node_modules\npm\node_modules\node-gyp\bin\node-gyp.js" rebuild --release )gyp ERR! find Pythongyp ERR! find Python Python is not set from command line or npm configurationgyp ERR! find Python Python is not set from environment variable PYTHONgyp ERR! find Python checking if "python" can be usedgyp ERR! find Python - "python" is not in PATH or produced an errorgyp ERR! find Python checking if "python2" can be usedgyp ERR! find Python - "python2" is not in PATH or produced an errorgyp ERR! find Python checking if "python3" can be usedgyp ERR! find Python - "python3" is not in PATH or produced an errorgyp ERR! find Python checking if the py launcher can be used to find Python 2gyp ERR! find Python - "py.exe" is not in PATH or produced an errorgyp ERR! find Python checking if Python is C:\Python27\python.exegyp ERR! find Python - "C:\Python27\python.exe" could not be rungyp ERR! find Python checking if Python is C:\Python37\python.exegyp ERR! find Python - "C:\Python37\python.exe" could not be rungyp ERR! find Pythongyp ERR! find Python **********************************************************gyp ERR! find Python You need to install the latest version of Python.gyp ERR! find Python Node-gyp should be able to find and use Python. If not,gyp ERR! find Python you can try one of the following options:gyp ERR! find Python - Use the switch --python="C:\Path\To\python.exe"gyp ERR! find Python (accepted by both node-gyp and npm)gyp ERR! find Python - Set the environment variable PYTHONgyp ERR! find Python - Set the npm configuration variable python:gyp ERR! find Python npm config set python "C:\Path\To\python.exe"gyp ERR! find Python For more information consult the documentation at:gyp ERR! find Python https://github.com/nodejs/node-gyp#installationgyp ERR! find Python **********************************************************gyp ERR! find Pythongyp ERR! configure errorgyp ERR! stack Error: Could not find any Python installation to usegyp ERR! stack at PythonFinder.fail (D:\nodejs\node_modules\npm\node_modules\node-gyp\lib\find-python.js:307:47)gyp ERR! stack at PythonFinder.runChecks (D:\nodejs\node_modules\npm\node_modules\node-gyp\lib\find-python.js:136:21)gyp ERR! stack at PythonFinder.<anonymous> (D:\nodejs\node_modules\npm\node_modules\node-gyp\lib\find-python.js:225:16)gyp ERR! stack at PythonFinder.execFileCallback (D:\nodejs\node_modules\npm\node_modules\node-gyp\lib\find-python.js:271:16)gyp ERR! stack at exithandler (child_process.js:315:5)gyp ERR! stack at ChildProcess.errorhandler (child_process.js:327:5)gyp ERR! stack at ChildProcess.emit (events.js:314:20)gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:274:12)gyp ERR! stack at onErrorNT (internal/child_process.js:468:16)gyp ERR! stack at processTicksAndRejections (internal/process/task_queues.js:80:21)gyp ERR! System Windows_NT 10.0.18363gyp ERR! command "D:\\nodejs\\node.exe" "D:\\nodejs\\node_modules\\npm\\node_modules\\node-gyp\\bin\\node-gyp.js" "rebuild" "--release"gyp ERR! cwd E:\lx-fr-admin-ui\node_modules\fibersgyp ERR! node -v v14.5.0gyp ERR! node-gyp -v v5.1.0gyp ERR! not oknode-gyp exited with code: 1Please make sure you are using a supported platform and node version. If youwould like to compile fibers on this machine please make sure you have setup yourbuild environment--Windows + OS X instructions here: https://github.com/nodejs/node-gypUbuntu users please run: `sudo apt-get install g++ build-essential`RHEL users please run: `yum install gcc-c++` and `yum groupinstall 'Development Tools'`Alpine users please run: `sudo apk add python make g++`'nodejs' 不是外部或外部命令,也不是可运行的程序或批处理文件。npm ERR! code ELIFECYCLEnpm ERR! errno 1npm ERR! fibers@4.0.3 install: `node build.js || nodejs build.js`npm ERR! Exit status 1npm ERR!npm ERR! Failed at the fibers@4.0.3 install script.npm ERR! This is probably not a problem with npm. There is likely additional logging output above.npm ERR! A complete log of this run can be found in:npm ERR! D:\nodejs\node_cache\_logs\2020-07-17T00_46_32_338Z-debug.log解决方案1.装置node-gypnpm install -g node-gyp2.装置python举荐装置2.7版本(自行抉择32位或者64位装置):https://www.python.org/downlo... ...

July 17, 2020 · 3 min · jiezi

搭建node服务三使用TypeScript

JavaScript 是一门动静弱类型语言,对变量的类型十分宽容。JavaScript应用灵便,开发速度快,然而因为类型思维的缺失,一点小的批改都有可能导致意想不到的谬误,应用TypeScript能够很好的解决这种问题。TypeScript是JavaScript的一个超集,扩大了 JavaScript 的语法,减少了动态类型、类、模块、接口和类型注解等性能,能够编译成纯JavaScript。本文将介绍如何在node服务中应用TypeScript。一、 装置依赖npm install typescript --savenpm install ts-node --savenpm install nodemon --save或者 yarn add typescriptyarn add ts-nodeyarn add nodemon另外,还须要装置依赖模块的类型库: npm install @types/koa --savenpm install @types/koa-router --save…或者 yarn add @types/koayarn add @types/koa-router…二、 tsconfig.json当应用tsc命令进行编译时,如果未指定ts文件,编译器会从当前目录开始去查找tsconfig.json文件,并依据tsconfig.json的配置进行编译。 1. 指定文件能够通过files属性来指定须要编译的文件,如下所示: { "files": [ "src/server.ts" ]}另外也能够通过应用"include"和"exclude"属性来指定,采纳相似glob文件匹配模式,如下所示: { "include": [ "src/**/*" ], "exclude": [ "node_modules", "**/*.spec.ts" ]}反对的通配符: 匹配0或多个字符(不包含目录分隔符)? 匹配一个任意字符(不包含目录分隔符)**/ 递归匹配任意子目录2. 罕用配置compilerOptions 属性用于配置编译选项,与tsc命令的选项统一,罕用的配置如下所示: { "compilerOptions": { // 指定编译为ECMAScript的哪个版本。默认为"ES3" "target": "ES6", // 编译为哪种模块零碎。如果target为"ES3"或者"ES5",默认为"CommonJS",否则默认为"ES6" "module": "CommonJS", // 模块解析策略,"Classic" 或者 "Node"。如果module为"AMD"、"System"或者"ES6",默认为"Classic",否则默认为"Node" "moduleResolution": "Node", // 是否反对应用import cjs from 'cjs'的形式引入commonjs包 "esModuleInterop": true, // 编译过程中须要引入的库。target为"ES5"时,默认引入["DOM","ES5","ScriptHost"];target为"ES6"时,默认引入["DOM","ES6","DOM.Iterable","ScriptHost"] "lib": ["ES6"], // 编译生成的js文件所输入的根目录,默认输入到ts文件所在的目录 "outDir": "dist", // 生成相应的.map文件 "sourceMap": true }, "include": [ "src/**/*" ], "exclude": [ "node_modules", "**/*.spec.ts" ]}1) targettarget是编译指标,能够指定编译为ECMAScript的哪个版本,默认为"ES3"。ECMAScript的版本有:"ES3" 、"ES5"、 "ES6" 或者 "ES2015"、 "ES2016"、 "ES2017"、"ES2018"、"ES2019"、 "ES2020"、"ESNext"。 ...

July 17, 2020 · 3 min · jiezi

Node-中如何更好地打日志

Node 中如何更好地打日志本文收录于 GitHub 山月行博客: shfshanyue/blog,内含我在理论工作中碰到的问题、对于业务的思考及在全栈方向上的学习 前端工程化系列Node进阶系列在服务器利用(后端我的项目)中,欠缺并结构化的日志不仅能够更好地帮忙定位问题及复现,也可能发现性能问题的端倪,甚至可能帮忙用来解决线上 CPU 及内存爆掉的问题。 本篇文章将解说如何应用 Node 在服务端更好地打日志 哪里应该打日志: AccessLog、SQLLog、BusinessLog应该打什么日志: server_name、timestamp 以及相干类型日志用什么去打日志: winston、log4j、bunyan产生日志后,将在下一章解说日志的收集解决及检索 日志类型在一个服务器利用中,或作为生产者,或作为消费者,须要与各方数据进行交互。除了最常见的与客户端交互外,还有数据库、缓存、音讯队列、第三方服务。对于重要的数据交互须要打日志记录。 除了外界交互外,本身产生的异样信息、要害业务逻辑及定时工作信息,也须要打日志。 以下简述须要打日志的类型及波及字段 AccessLog: 这是最常见的日志类型,个别在 nginx 等方向代理中也有日志记录,但在业务零碎中有时须要更具体的日志记录,如 API 耗时,具体的 request body 与 response bodySQLLog: 对于数据库查问的日志,记录 SQL、波及到的 table、以及执行工夫,从此能够筛选出执行过慢的SQL,也能够筛选出某条API对应的SQL条数RequestLog: 申请第三方服务产生的日志Exception: 异样RedisLog: 缓存,也有一些非缓存的操作如 zset 及分布式锁等Message Queue Log: 记录生产音讯及生产音讯的日志CronLog: 记录定时工作执行的工夫以及是否胜利要害业务逻辑日志的根本字段对于所有的日志,都会有一些共用的根本字段,如在那台服务器,在那个点产生的日志 app即以后我的项目的命名,在生产环境有可能多个我的项目的日志聚合在一起,通过 app 容易定位到以后我的项目 serverName即服务器的 hostname,通过它很容易定位到出问题的服务器/容器。 现已有相当多公司的生产环境利用应用 kubernetes 进行编排,而在 k8s 中每个 POD 的 hostname 如下所示,因而很容易定位到 Deployment: 哪一个利用/我的项目ReplicaSet: 哪一次上线Pod: 哪一个 Pod# shanyue-production 指 Deployment name# 69d9884864 指某次降级时 ReplicaSet 对应的 hash# vt22t 指某个 Pod 对应的 hash$ hostnameshanyue-production-69d9884864-vt22ttimestamp即该条日志产生的工夫,应用 ISO 8601 格局有更好的人可读性与机器可读性 ...

July 15, 2020 · 3 min · jiezi

linux-centOS-安装node-ffi解析so文件

// 运行环境:linux centOS 7 64位 // 首先切换到root模式su - root,提醒输出明码,实现之后进行以下操作。 // 下载8.6.01.wget https://nodejs.org/dist/v8.6....// 解压2.xz -d node-8.6.0-linux-x64.tar.xz 3.tar -xf node-v8.6.0-linux-x64.tar// 重命名4.mv node-v8.6.0-linux-x64 node-v8.6.0// 增加到全局应用5.ln -s /root/node-v8.6.0/bin/node /usr/local/bin/node 6.ln -s /root/node-v8.6.0/bin/npm /usr/local/bin/npm // 装置ffi(要求node版本8-10,python2.7,g++运行环境)1.首先装置node-gyp到全局,并增加到全局应用npm i node-gyp -gln -s /root/node-v8.6.0/bin/node-gyp /usr/local/bin/node-gyp2.装置g++yum i gcc-c++3.装置ffi,敞开权限平安校验进入到文件目录,npm i ffi -unsafe perm,如果报错Cannot find module 'nan',就npm i nan,而后再从新执行npm i ffi -unsafe perm。

July 14, 2020 · 1 min · jiezi

Nodejs写接口时配置静态文件路径

Nodejs写接口时配置动态文件门路须要应用 express要害代码const express = require('express');const app = express();app.use(express.static(__dirname + '/public'));当初就能够加载public目录下的动态文件了: http://127.0.0.1:8100/images/someimg.jpg Express 会在动态资源目录下查找文件,所以不须要把动态目录作为URL的一部分。虚构动态目录如果要给动态资源文件创建一个虚构的文件前缀(实际上文件系统中并不存在) ,能够应用 express.static 函数指定一个虚构的动态目录,语法如下: app.use('/static', express.static(__dirname + '/public'));当初能够应用 /static 作为前缀来加载 public 文件夹下的文件了: http://127.0.0.1:8100/static/images/someimg.jpg 增加多个动态目录能够通过屡次应用 express.static 中间件来增加多个动态资源目录: app.use(express.static('public'));app.use(express.static('files'));

July 13, 2020 · 1 min · jiezi

Nginx深入详解之upstream分配方式

### 一、调配形式 Nginx的upstream反对5种调配形式,上面将会具体介绍,其中,前三种为Nginx原生反对的调配形式,后两种为第三方反对的调配形式:### 1、轮询轮询是upstream的默认调配形式,即每个申请依照工夫程序轮流调配到不同的后端服务器,如果某个后端服务器down掉后,能主动剔除。upstream backend { server 192.168.1.101:8888; server 192.168.1.102:8888; server 192.168.1.103:8888;}### 2、weight轮询的加强版,即能够指定轮询比率,weight和拜访几率成正比,次要利用于后端服务器异质的场景下。 upstream backend { server 192.168.1.101 weight=1; server 192.168.1.102 weight=2; server 192.168.1.103 weight=3;}### 3、ip_hash每个申请依照拜访ip(即Nginx的前置服务器或者客户端IP)的hash后果调配,这样每个访客会固定拜访一个后端服务器,能够解决session统一问题。upstream backend { ip_hash; server 192.168.1.101:7777; server 192.168.1.102:8888; server 192.168.1.103:9999;}### 4、fairfair顾名思义,偏心地依照后端服务器的响应工夫(rt)来调配申请,响应工夫短即rt小的后端服务器优先调配申请。 upstream backend { server 192.168.1.101; server 192.168.1.102; server 192.168.1.103; fair;}### 5、url_hash与ip_hash相似,然而依照拜访url的hash后果来调配申请,使得每个url定向到同一个后端服务器,次要利用于后端服务器为缓存时的场景下。upstream backend { server 192.168.1.101; server 192.168.1.102; server 192.168.1.103; hash $request_uri; hash_method crc32;}其中,hash_method为应用的hash算法,须要留神的是:此时,server语句中不能加weight等参数。### 二、设施状态从下面实例不难看出upstream中server指令语法如下: server address [parameters]关键字server必选。address也必选,能够是主机名、域名、ip或unix socket,也能够指定端口号。parameters是可选参数,能够是如下参数:down:示意以后server已停用backup:示意以后server是备用服务器,只有其它非backup后端服务器都挂掉了或者很忙才会调配到申请。weight:示意以后server负载权重,权重越大被申请几率越大。默认是1.max_fails和fail_timeout个别会关联应用,如果某台server在fail_timeout工夫内呈现了max_fails次连贯失败,那么Nginx会认为其曾经挂掉了,从而在fail_timeout工夫内不再去申请它,fail_timeout默认是10s,max_fails默认是1,即默认状况是只有产生谬误就认为服务器挂掉了,如果将max_fails设置为0,则示意勾销这项查看。举例说明如下:upstream backend { server backend1.example.com weight=5; server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; server unix:/tmp/backend3; }

July 13, 2020 · 1 min · jiezi

Electron实现音乐播放器-2

实现简略的音乐播放找一个简略的mp3文件作为示例: 能够简略的应用audio标签来播放mp3文件: <!-- index.html --><!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <audio controls> <source src="horse.mp3" type="audio/mpeg"> </audio></body></html>效果图: 自定义播放文件咱们不可能永远的只播放horse.mp3这个示例,咱们心愿用户可能通过会话窗口来关上想要关上的音乐文件,实现音乐播放器的核心内容 自定义菜单栏咱们想要实现自定义播放内容,首先须要一个咱们本人的菜单栏,来给用户一个关上文件的入口 首先引入Menu模块: // main.jsconst electron = require('electron') //引入electron模块const { app, BrowserWindow, Menu} = electron //从electron中引入咱们须要的模块let mainWindow; //申明一个变量,作为主窗口app.on('ready', function () { //当app筹备好时执行 mainWindow = new BrowserWindow({ //将mainWindow定义为一个新的浏览器窗口 //这一部分先空着,前面会向窗口增加参数 }) mainWindow.loadFile("index.html");})而后构建咱们的菜单: // main.jsconst electron = require('electron') //引入electron模块const { app, BrowserWindow, Menu} = electron //从electron中引入咱们须要的模块let mainWindow; //申明一个变量,作为主窗口app.on('ready', function () { //当app筹备好时执行 mainWindow = new BrowserWindow({ //将mainWindow定义为一个新的浏览器窗口 //这一部分先空着,前面会向窗口增加参数 }) mainWindow.loadFile("index.html"); mainMenu = Menu.buildFromTemplate(MenuTemplate)//应用Menu模块的内置函数将json格局的菜单模板转换为electron的菜单格局 mainWindow.setMenu(mainMenu)//把菜单插入窗口})const MenuTemplate = [{//菜单模板,json格局 label: "File", submenu: [{//菜单项的子菜单,同json格局 label: "Open File" }]}]效果图: ...

July 9, 2020 · 2 min · jiezi

快速搭建koa项目

一、安装koa生成器npm install koa-generate -g# 查看是否安装成功kao2 -V二、创建项目// 命令 koa2 [name] [-e] e表示使用ejs引擎koa2 hello -e三、安装与运行# 进入项目cd hello# 安装依赖npm install# 运行npm start 四、使用token服务端配置 const Jwt = require('koa-jwt');const Jsonwebtoken = require('jsonwebtoken');//使用jwtapp.use(Jwt({ secret: 's' }).unless({ // 过滤不需要验证的路由 path: [ /^\/public\/login/ ... ]}))// 获取tokenapp.use(async (ctx, next) => { // token解密,获取用户信息 let token = ctx.headers.authorization let user = Jsonwebtoken.verify(token.split(' ')[1], 's'); ... await next()})客户端配置 axios.interceptors.request.use(config => { config.header.Authorization = 'token...' return config;}, error => { return Promise.reject(error);})五、配置路由删除原有的路由配置,修改为动态读取 ...

July 8, 2020 · 1 min · jiezi

anyproxy-转发body

之前一直使用 fiddler 来抓包数据并转发到自建server,但 fiddler 缓存太大,只能通过关闭重启 fiddler 才能清除缓存,电脑内存不够是很麻烦的事.现在有了 anyproxy 就解决了缓存问题.anyproxy 是和 fiddler 基本作用一样的抓包工具,安装不介绍了,都能搜到,写写 rule.js 的二次开发. anyproxy 默认启动加载的规则文件路径在 C:\Users\li\AppData\Roaming\npm\node_modules\anyproxy\lib\rule_default.js我们做数据加工或者转发可通过自建 js 来实现.以windows为例:1.可在桌面创建 sample.js, 将数据处理逻辑写入 sample.js中.2.打开 cmd,进入桌面,或者不进入桌面,第 3 步写 sample.js 觉得路径也行.3.通过 sample.js 启动 anyproxy,启动命令: anyproxy -i --rule ./sample.js4.浏览器登录 127.0.0.1:8002 就能更直观的看到抓包的数据,浏览器作为调试使用,真正抓包时不用开启. 说下我踩的坑: anyproxy 是使用的 node.js 语法,所以编辑 sample.js 时如果使用到 js 的 ajax 语法是无效的. 下面贴一段我使用的抓包数据 body, 并通过 node.js post 请求将数据转发到自建的 server 进行解析. // sample.jsmodule.exports = { //summary: 'customized wechat request', // beforeSendResponse: 在数据获取成功并解析成功后准备返回给 cli 之前做数据处理. *beforeSendResponse(requestDetail, responseDetail) { // 当 anyproxy 匹配到 url 地址中含有 /aw/v4/aw/post/,将数据 body 通过 node.js 以json字符串 post 到自建server if (requestDetail.url.indexOf('/aw/v4/aw/post/') != -1) { var data = { str: responseDetail.response.body.toString() }; // parse 为自己 server 路径 let res = sendPostHttpRequest(data, 'parse'); } },};// node.js 的 post 请求,不能用 js 的 ajax 请求function sendPostHttpRequest(body, route) { var http = require('http') var querystring = require('querystring'); let options = { hostname: '127.0.0.1', port: 5001, path: '/' + route, method: 'POST', headers: {"content-type": 'application/x-www-form-urlencoded'} }; var contents = querystring.stringify(body); var req = http.request(options, function(res){ res.setEncoding('utf8'); }); req.write(contents); req.end();};

July 7, 2020 · 1 min · jiezi

Node

尚硅谷-Node.js基础入门一杯茶的时间,上手Node.js Node简介Node可以在后台编写服务 进程进程负责为程序运行提供必要的环境进程就相当于工厂中的车间线程线程是计算机中最小的计算单位,线程负责执行进程中的程序线程就相当于工厂中的工人单线程 js是单线程多线程 Java/Python都是多线程语言传统的服务器都是多线程的每进来一个请求,就创建一个线程去处理请求Node服务器是单线程的Node处理请求时是单线程,但是在后台拥有一个I/O线程池特征异步、事件驱动、非阻塞IO模块化在node中,一个js文件就是一个模块在node中每一个js代码都是独立运行在一个函数中 而不是全局作用域,所以一个模块中的变量和函数在其他模块中无法访问 不要总是往全局中写东西,会污染全局作用域,污染全局命名空间,这种模块化的方式可以避免污染全局命名空间CommonJs对模块的定义十分简单 模块的引用 reuqire()模块的定义 创建一个js文件 export添加属性或方法进行输出模块的标识01.helloNode.j //引入其他模块/* 1、在node中,通过require()函数引入外部模块 2、require()可以传递一个文件的路径作为参数, node将会自动根据该路径来引入外部模块 这里的路劲,如果是相对路劲,必须以./ 或者 ../开头 3、使用require()引入一个模块以后,该函数会返回一个对象, 这个对象代表的是引入的模块 4、我们使用require()引入外部模块时,使用的就是模块标识,我们可以通过标识来找到指定的模块 -没看分为两大类 核心模块 -由node引擎提供的模块 // var fs = require('fs'); console.log(fs) //文件模块 不用写路劲直接写名字就行 // var path = require('path'); //路劲模块 // var http = require('http'); //服务模块 -核心模块的标识就是模块的名字 文件模块 - 由用户自己创建的模块 - 文件模块的标识就是文件的路径(绝对路径,相对路径) 相对路径使用 ./ 或者 ../ 开头*/var md = require("./02.module.js");console.log(md)02.module.js console.log('我是一个模块,我是02.module.js');/* 我们可以通过exports来向外部暴露暴露变量和方法 只需要将需要暴露给外部的变量或方法设置为exports的属性即可*///向外部暴露属性或者方法export.x = "我是02.module.js中得x";export.y = "我是02.module.js中得y";包package包结构– package.json:描述文件,必须– bin:可执行二进制文件 这里边的文件都是可以直接在系统中运行的 //一般不会有这个文件,除非是一些工具 webpack– lib:依赖的其他js代码,library(图书馆,库),非必须– doc:文档,非必须– test:单元测试,非必须包规范npmnpm -v:查看npm版本npm version:查看所有模块的版本npm seaarch math:搜索math包npm init:初始化package.jsonnpm install math(npm i math):安装math包,安装包的时候是根据package.json来识别的,没有该文件的话不能识别这是一个包,就会全局安装npm remove math(npm r math):删除math包npm i math --save(npm i math -S):安装到生成依赖npm install:自动根据package.json中的依赖下载所有的包npm install math -g:全局安装包(全局安装的包一般都是一些工具)通过npm下载的包都放到node_modules文件夹中我们通过下载的包,直接通过包名引入即可 // var express = require('express')node在使用模块名字引入模块时,他会首先在当前目录的node_modules中寻找是否含有该模块如果有则直接使用,如果没有则去上一层的node_modules中寻找如果有则直接使用,如果没有则在去上一层寻找,直到找到为止直到磁盘根目录中,如果依然没有找到,则报错index.js 入口文件 ...

July 5, 2020 · 2 min · jiezi

Electron实现音乐播放器-1

编写简单的页面初始化项目文件夹创建一个文件夹,进入这个文件夹并通过如下命令初始化项目 npm init package name:项目名称,可自设version:版本号,可自设description:描述,可不填entry point:入口文件,这里我们填main.jstest command:可不填git repository:git仓库,可不填keywords:关键词,可不填author:作者,可自设license:开源协议,可不填输入yes完成初始化,随后会自动生成package.json文件在文件夹内,这是我们项目的描述文件 编写简单的main.js文件main.js是我们项目的入口文件(主文件),而项目启动时运行main.js的进程就是主进程,创建窗口、对渲染进程进行调度等等一系列操作都要依靠main.js来实现 在文件夹内创建main.js文件并编辑它: const electron = require('electron')//引入electron模块const {app,BrowserWindows} = electron//从electron中引入我们需要的模块let mainWindow;//声明一个变量,作为主窗口app.on('ready',function(){//当app准备好时执行 mainWindow = new BrowserWindow({//将mainWindow定义为一个新的浏览器窗口 //这一部分先空着,后面会向窗口添加参数 })})第一、二行引入了我们所需要的模块,然后我们声明了一个mainWindow变量,并在app准备好时将mainWindow定义为一个BrowserWindow对象,也就是一个浏览器窗口 运行和安装依赖我们可以通过如下命令来安装electron模块: cnpm install electron --save-dev接着我们可以通过如下命令来运行我们的项目: electron .注意electron和.之间有一个空格! 效果图: 我们会发现只是弹出了一个窗口,里面并没有什么内容,这是因为我们并没有指定它需要加载的内容 Hello,World!我们可以通过loadURL函数来让我们的应用程序窗口来通过链接加载一个网页 const electron = require('electron')//引入electron模块const {app,BrowserWindow} = electron//从electron中引入我们需要的模块let mainWindow;//声明一个变量,作为主窗口app.on('ready',function(){//当app准备好时执行 mainWindow = new BrowserWindow({//将mainWindow定义为一个新的浏览器窗口 //这一部分先空着,后面会向窗口添加参数 }) mainWindow.loadURL("https://www.baidu.com");})效果图: 但是很显然,我们不能只满足于加载现成的网页,我们可以通过加载HTML文件来达到自定义页面的目的 创建一个HTML文件,编写一个简单的例子: <!-- index.html --><!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <h1>Hello,World!</h1></body></html>文件夹结构: 通过loadFile函数来加载HTML文件: const electron = require('electron')//引入electron模块const {app,BrowserWindow} = electron//从electron中引入我们需要的模块let mainWindow;//声明一个变量,作为主窗口app.on('ready',function(){//当app准备好时执行 mainWindow = new BrowserWindow({//将mainWindow定义为一个新的浏览器窗口 //这一部分先空着,后面会向窗口添加参数 }) mainWindow.loadFile("index.html");})效果图: ...

July 4, 2020 · 1 min · jiezi

nodejs负载均衡二RPC负载均衡

简介这一篇确实拖的比较久,上节讲了服务负载均衡实现,但是如果需要调用远程服务, 如何保证不是调用不会集中在一台服务上,如何确保远程服务调用的负载均衡?这就要实现 Consumer 端调用rpc的负载均衡。所以本文章主要讲解 RPC负载均衡算法实现 。 算法下面介绍几个主要的负载均衡算法如何实现,可以看下我写的NPM包 load-balancer-algorithm。 const LBA = require('load-balancer-algorithm');const weightRandomPool = [ { host: "127.0.0.2", weight: 2 }, { host: "127.0.0.1", weight: 3 }, { host: "127.0.0.3", weight: 5 },];const weightedList = []const loadBalance = new LBA.WeightedRoundRobin(weightRandomPool);for(let i = 0; i < 10; i++){ const address = loadBalance.pick(); weightedList.push(loadBalance.getWeight(address.host))}// [5, 5, 3, 5, 2, 3, 5, 2, 3, 5]console.log(weightedList)Round Robin轮询(Round robin) 算法,就是依次轮询服务队列的节点,周而复始,这个实现比较简单。 消费端调用均衡通过当前下标+1对数据池长度数取模,获取到下一个节点下标缺点: 要求服务提供节点性能一致 Weighted Round Robin权重轮询(Weighted round robin) 算法,基于轮询添加权重判断,这样可以针对性能低服务节点减少流量。 ...

June 30, 2020 · 1 min · jiezi

jwt登陆校验携带token

jwt登陆注册jwt概念token是不需要存储在数据库的,只需要后台生成密钥,当客户端发送过来请求时那么就把token塞在请求体或者头中,客户端接收到token时那么就存储在localStorage或者cookie中,注意这里不能把敏感信息进行token存储,要不然会被获取到(比如cookie中或者请求体中)并且进行反向解密从而暴露敏感信息,比如密码; token中包含三块都是以 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9//头信息这个是固定写死的{'type': 'JWT','alg': 'HS256'}.> eyJ1c2VyIjoid2FuZyIsImVudmlyb25tZW50Ijoid2ViIiwiZXhwaXJlcyI6MTUzNTUyMDk1MTcxOX0//载体 就是比如客户端发过来的username.> 4rmP6UeeJ3mQbHfplruH15PvuUxPzFTxAfVnPgA7sgo//密钥根据将头和载体进行转换base64编码,然后在将头和载体根据sha256算法加密生成密钥(也就是第三步)这样就组成了一个token,将这种思路换成代码就会实现jwt-smilp包中的encode,那么decode就是获取到进行反向解码jwt-smilp中的encode和decode{username:username}//载体 载体中还可以设置时间以及其他 jwt.encode({username:username},sercet)//sercet自己定义的密钥 jwt.encode(token,sercet)//进行解密登陆注册全过程进入登陆页,输入账号和密码点击登陆请求接口,后台会在数据库中寻找对应的账号密码 如果存在那么后台会生成一个token并且塞在请求头(或者响应体中),给前端之后,前端获取返回值 并且存放在cookie中或者请求头中,前端通过axios中的请求拦截给请求头中塞进密钥,这样每次发送请求就会携带token,后台接收到token之后会进行解码校验是否是正确的token,如果是的话就进行下一步操作,如果不是的话就给前端提示 node后台代码的实现将node框架搭好之后新建一个src的文件下新建一个app.js的文件代码如下 let express = require('express')let app = express()//获取req的bodylet bodyParser = require('body-parser');//引入自己写的登陆校验是否合规文件let retoken = require('./retoken/retoken.js')//bodyParser对应的以下两个也是解析post中的json数据app.use(express.json())//urlencoded解析x-ww-form-urlencoded请求体app.use(express.urlencoded())//返回的对象是一个键值对,当extended为false的时候,键值对中的值就为'String'或'Array'形式,为true的时候,则可为任何数据类型app.use(bodyParser.urlencoded({ extended: true }))//使用上面自己的写的中间件app.use(retoken)//登陆接口文件app.use('/login', require('./login/index.js'));//统一处理错误信息app.use((err, req, res, next) => { if (err) { res.status(500).json({ message: err.message }) }})//默认监听3000端口app.listen(3000, () => { console.log('服务启用成功');})retoken.js//使用第三方插件let jwt = require('jwt-simple');//定义加密和解密的密钥(随便写)const jstSecret = 'mengyuhang' function checkToken(req,res,next){ //判断如果是登陆接口或者注册接口那么就不需要校验token if(req.url!='/login/login'&&req.url!='/login/create'){ //这里前端是直接将token塞到了cookie中所以我直接在cookie中获取 let tokenClone = req.headers.cookie;//token传递过来的为这种形式:token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Im15aDEyMyIsImV4cGlyZXMiOjE1OTQwNDU4NDc1NzN9.PwIAp3nXIscIvgXykjQumO6CbIFceHpGz6-2PUHgQU4//截取等号后面的 let token = tokenClone.split('=')[1] if(token){ //如果存在那么解码,我这里加密的用户名和校验时间是否过期{ username: 'myh123', expires: 1594045847573 } var decoded = jwt.decode(token, jstSecret) //如果现在的时间超过了我设置的登陆时间那么就返回登陆过期 if(Date.now()>decoded.expires){ res.json({ code:'-2', message: '登陆过期' }) }else{ //否则向下执行 next() } }else{ //如果没有token那么返回提示 res.status(401).json({ code:"-1", message: 'you need login:there is no token' }) } }else{ //如果为登陆或者注册接口直接向下进行 next() } }module.exports=checkTokenlogin.js中的接口书写const express = require('express');let router = express.Router();//我这里使用sequelize操作数据库let sequelize = require('sequelize')let models = require('../../db/models')let jwt = require('jwt-simple');//设置token的过期时间const tokenExpiresTime = 1000 * 60 * 60 * 24 * 7//密钥const jstSecret = 'mengyuhang'router.post('/login', async (req, res, next) => { let { username, password} = req.body; try { //在数据库中的user表中寻找对应的账号密码 let personalInformation = await models.User.findOne({ where: { username, password } }) //如果账号存在 if (personalInformation) { //生成token //需要加密的对象 let payload = { username: personalInformation.username, expires: Date.now() + tokenExpiresTime } //jwt-simple包提供的加密方法,jstSecret自己定义的密钥 let token = jwt.encode(payload, jstSecret) res.json({ message: '登陆成功', token }) } else { //如果账号不存在 res.json({ code: '-1', message: '用户不存在,请校验信息是否正确' }) } } catch (e) { res.json({ message: '错误' }) }})//注册接口router.post('/create', async (req, res, next) => { let { username, password } = req.body; try{ //在数据库中寻找用户名 let user = await models.User.findOne({ where: { username } }) //如果用户名存在 if (user) { res.json({ code: '-1', message: '用户名已存在' }) } else { //如果用户名不存在就可以注册成功啦 await models.User.create({ username, password }) res.json({ code: '0', message: '注册成功' }) } }catch(e){next() } })module.exports = router前端登陆点击登陆时获取到后台返回的token之后直接塞在document.cookie中即可 ...

June 30, 2020 · 2 min · jiezi

使用nodejs实现JSON文件自动转Excel的工具

这段时间做项目,需要把json格式的文档给到业务人员去翻译,每次手动翻译,很麻烦,于是就想着写一个高逼格的自动化工具来完成这件事情。说实现,初步思路就是使用类似"json2excel start"这样的命令,然后它就自己跑。像vue,react运行命令一样。 首先,我们npm init 新建一个项目工程,新建我们项目的核心文件json2excel.js,并运行node json2exce.js,然后控制台就可以打印东西了。 把一个文件转化成另一个文件,我们要知道这个文件的路径,以及保存到的位置,所以命令设计为: json2excel start inpath outpath我们使用一个非常好用的命令行辅助包"commander",提示命令输入,json2excel.js如下, const program = require('commander')// 定义当前的版本program .version(require('../package').version)// 定义命令方法program .usage('<command> [inPath] [toPath]') program .command('start [paths...]') .description('Conversion from JSON to csv') .alias('-s') .action(paths => require('./command/j2c')(paths)) program.parse(process.argv)if (!program.args.length) { program.help()}然后运行node json2excel.js会看到(现在还没安装bin命令,所以用node json2excel代替json2excel), 非常哇瑟的一个操作,就可以看到命令引导提示了。.command() 是定义命令及其后面的参数,我们定义了paths.description() 是描述.alias() 是命令的别名.action() 是运行命令时要执行的操作,paths是command里面传过来的参数 我们新建../command/j2c.js,.action()的时候我们有接受命令参数 module.exports = (paths) => { // 这样就得到了输入、输出的路径了 let [inPath, outPath] = paths}如果命令参数没有附带怎么办?如:node json2excel start 不带路径然后就回车 那我们就引导用户再次输入,使用"co","co-prompt"这两个工具 上代码:../command/j2c.js const co = require('co')const prompt = require('co-prompt')module.exports = (paths) => { co(function* () { let [inPath, outPath] = paths // 处理用户输入 inPath = inPath ? inPath : yield prompt('Input file directory: ') outPath = outPath ? outPath : (yield prompt('Output file directory: ')) || inPath })}co里面接受generator函数,主要是异步操作作同步处理的写法。 ...

June 23, 2020 · 3 min · jiezi

记录在阿里上开服务的过程

从5、6年前就开始想办法做一个个人网站。直到今天才有了一点样子。自己是从一个前端走来的。前端对于一个网站中的占比太小了。要做好网站,有太多非前端的事儿要做。下面记录一次在阿里云上部署node服务的过程。 学习node从前端向后端走,选择node是一个比较好的选择。它有天然的语言衔接。为了做网站,可以再学习一个框架。如:express/koa.我就是学习的express。 在本地开发完网站买一个阿里云的服务注册一个阿里云的账号并登录买一个服务器我买的轻量应用服务器(它比较便宜)。选“地域”这名字一看就知道是选择哪里的服务器。随便选吧。“选择镜像”选项是“系统镜像”、“应用镜像”。若没有“镜像”我就知道是选什么,非要加一个“镜像”。我去~,尽为难我们不懂服务端的人。我就当选择“系统”、“应用”去选择的。再选择时长。然后就是花钱吧。 配置服务器然后进入“控制台”,再进入“轻量应用服务器”,再进入你刚买到的服务里。再进入“应用详情”里,点击“远程登录服务器”,可以在浏览器中登录服务器。在"远程连接"页面里,点击“设置密码”,再输入相应密码。就可以使用客户端登录远程服务器了。 ssh root@47.47.47.47// ssh 账号@公网ip// 输入密码上传代码有几种上传代码的方式。目的都是让服务器上有代码。 使用scpscp -r myapp root@47.47.47.47:/var/www 使用git安装git配置git// 代码一般放在/var/www里// cd /var/wwwgit config --global user.name "yourname"git config --global user.email "youremail@example.com"git config --global --list // 查看配置结果拉取仓库中的代码使用jenkins我不会 自己做个小的应用,接收git的推送事件,自动执行相应的脚本我不会 配置服务器环境安装node安装npm安装git安装nvm全局安装pm2启动服务pm2 start <path/to/server>

June 23, 2020 · 1 min · jiezi

nodejsexpress项目中对特定请求使用不同的bodyparser处理

node+express 项目在全局使用了bodyParser.json()和bodyParser.urlencoded()。但现在碰到个需求,对某些特定前缀的url请求,需要拿到Buffer格式的请求体,而不是json格式化后的。 body-parser官方文档中有写如何针对不同请求使用不同的解析: var express = require('express')var bodyParser = require('body-parser') var app = express() // create application/json parservar jsonParser = bodyParser.json() // create application/x-www-form-urlencoded parservar urlencodedParser = bodyParser.urlencoded({ extended: false }) // POST /login gets urlencoded bodiesapp.post('/login', urlencodedParser, function (req, res) { res.send('welcome, ' + req.body.username)}) // POST /api/users gets JSON bodiesapp.post('/api/users', jsonParser, function (req, res) { // create user in req.body})但是项目中用到了express的路由,没找到如何直接针对特定路由改写的方法,看到一篇文章,里面配置项中的verify(req, res, buf, encoding)函数的第三个参数可以拿到raw body,但是缺点文章中也说了,并且对所有请求都生效了。 ...

June 18, 2020 · 1 min · jiezi

nodeeggjs下使用nsq-实现puppteer生成pdf服务

本篇文章主要介绍如何在nodeJs中使用nsq,其他实现将在后续文章输出。 起因前段时间做了一个网页生成pdf的node服务。由于puppteer和canvas生成过程中对内存的消耗比较大,内容量大的网页生成时间过长,对于第三方组件有时候会生成出问题等原因。 引入了nsq,使项目实现负载均衡,消除单点故障。 但是网上查找之后发现介绍node中加入nsq的方案很少,经过软膜硬泡终于把nsq引入了node中,希望能把自己的收获和大家聊一下吧。 初识nsqNSQ是一个基于Go语言的分布式实时消息平台, 它具有分布式、去中心化的拓扑结构,支持无限水平扩展。无单点故障、故障容错、高可用性以及能够保证消息的可靠传递的特征。另外,NSQ非常容易配置和部署, 且支持众多的消息协议。支持多种客户端,协议简单。 nsq设计很简单,需要了解以下几个核心概念。 1、nsqd:一个负责接收、排队、转发消息到客户端的守护进程 2、nsqlookupd:管理拓扑信息, 用于收集nsqd上报的topic和channel,并提供最终一致性的发现服务的守护进程。 3、Topic:一个topic就是程序发布消息的一个逻辑键,当程序第一次发布消息时就会创建topic。4、Channels:channel组与消费者相关,是消费者之间的负载均衡,channel在某种意义上来说是一个“队列”。每当一个发布者发送一条消息到一个topic,消息会被复制到所有消费者连接的channel上,消费者通过这个特殊的channel读取消息,实际上,在消费者第一次订阅时就会创建channel。Topic只能有一个channel可以有多个,不同的channel用于分发不同的任务下面附上一个金典nsq示意图: 安装中遇到的问题在使用过程中发现gcc版本过低导致报错。因为node.js 4升级了v8引擎,需要gcc版本在4.8以上。 实战开始本项目是在eggjs基础上建立的。首先需要在项目中安装nsqjs $ npm install nsqjs --save在根文件app.js对nsq进行控制,nsq的配置非常简洁,nsq分为写和读两个独立的过程。 const nsq = require('nsqjs')module.exports = app => { app.beforeStart(async () => { // 实例化nsq的写操作 // 在config中配置nsq的host和port,这里是你配置的nsq地址 const writerNsq = new nsq.Writer(app.config.nsq.nsqHostWriter, app.config.nsq.writePort) // 连接nsq的写功能 writerNsq.connect() // 当写操作连接成功后,把其赋值到全局的app上以便写入信息 writerNsq.on('ready', () => { app.writerNsq = writerNsq })当nsq写功能实现后,我们可以通过publish方法向nsq队列写入信息 ctx.app.writerNsq.publish(config.nsq.topic, { // 你所需要传递的参数 })当服务器有空闲的时候nsq会随机分配到空闲线程上实现读操作,我们的核心业务是在读功能启用之后实现的。读和写的初始化过程是在项目启动时候就要执行的。项目运行过程中,我们只是不停的在进行读写操作而已。 // 实例化nsq的读操作 // 参数是对应需要读的topic和channel和应的nsq读的地址 const client = new nsq.Reader(app.config.nsq.topic, app.config.nsq.channel, { lookupdHTTPAddresses: app.config.nsq.nsqHostReader, maxInFlight: 1 }) // 连接nsq的读功能 client.connect() // 每当有消息队列进来的时候就会调用client.on方法 // message是我门在写的过程中传入的信息 client.on方法('message', async msg => { // 对写入的信息格式化 let data = JSON.parse(msg.body.toString()) try { // 为了保持连接状态,处理超时情况 const touch = () => { if (!msg.hasResponded) { msg.touch() // Touch the message again a second before the next timeout. setTimeout(touch, msg.timeUntilTimeout() - 1000) } } let timeTouch = setTimeout(touch, msg.timeUntilTimeout() - 1000) let timeFinish = setTimeout(msg.finish.bind(msg), msg.timeUntilTimeout() * 3 + 1000) // 这里是项目的核心处理部分,具体内容后面文章在说明,这里返回的url便是生成pdf的网络地址 let url = await ctx.service.pdf.index.generate(data) clearTimeout(timeTouch) clearTimeout(timeFinish) // 这里表示这个队列结束告诉nsq可以放下个兄弟进来了 msg.finish() } catch (error) { // 万一出现错误也不要阻塞,nsq在失败后会重新入队 msg.finish() // 这里可以加入网络日志 console.log(error) } }); client.on('error', function(err) { // 这里监听读操作时候发生错误情况处理 // 这里可以做一些错误处理,加错误日志 console.log(err) }); });};刚开始说到用nsq还是有点慌的,毕竟作为一个前端工程师一脸懵逼,但是经过仔细学习,和后端大佬的请教,发现nsq其实就是一个高效队列,简易好用,对于上手来说还是比较简单的。后续文章还会对puppteer生成pdf服务的核心业务做详细介绍,也就是上文ctx.service.pdf.index.generate(data)具体实现过程。以上只是本人的学习总结,如有问题,请大神不吝赐教。 ...

June 18, 2020 · 1 min · jiezi

mongodb数据关联

const mongoose = require('mongoose');mongoose.connect('mongodb://localhost: 27017/mongo-relation', { useNewUrlParser: true, useUnifiedTopology: true }, err => { if (err) { console.log('数据库连接失败'); } console.log('数据库连接成功');})const categorySchema = new mongoose.Schema({ name: { type: String }}, { toJSON: { virtuals: true }});categorySchema.virtual('posts', { // 本地键 localField: '_id', // 关联的模型 ref: 'Post', // 外键 foreignField: 'categories', // 输出多个 justOne: false});const Category = mongoose.model('Category', categorySchema);const postSchema = new mongoose.Schema({ title: { type: String }, body: { type: String }, category: { type: mongoose.SchemaTypes.ObjectId, ref: 'Category' }, categories: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'Category' }]});const Post = mongoose.model('Post', postSchema);async function init() { // const cats = await Category.find().populate('categories'); // console.log(cats); // [ // { _id: 5ee0a7f22adf1624e8b9b64d, name: 'nodejs', __v: 0 }, // { _id: 5ee0a7f22adf1624e8b9b64e, name: 'vue.js', __v: 0 } // ] // 加上lean() // const cats = await Category.find().populate('posts').lean(); // console.log(cats); // [ // { // _id: 5ee0a7f22adf1624e8b9b64d, // name: 'nodejs', // __v: 0, // posts: [ [Object] ] // }, // { // _id: 5ee0a7f22adf1624e8b9b64e, // name: 'vue.js', // __v: 0, // posts: [ [Object], [Object] ] // } // ] // cats[0].posts 查找第1个分类内容 // const cats = await Category.find().populate('posts').lean(); // console.log(cats[0].posts); // [ // { // _id: 5ee0a74dc6a921238454fa52, // title: '第2篇文章', // body: '内容2', // __v: 9, // category: 5ee0a7f22adf1624e8b9b64d, // categories: [ 5ee0a7f22adf1624e8b9b64d, 5ee0a7f22adf1624e8b9b64e ] // } // ] // JSON.stringify const cats = await Category.find().populate('posts').lean(); console.log(JSON.stringify(cats)); // [{"_id":"5ee0a7f22adf1624e8b9b64d","name":"nodejs","__v":0, // "posts":[{"_id":"5ee0a74dc6a921238454fa52", // "title":"第2篇文章","body":"内容2","__v":9, // "category":"5ee0a7f22adf1624e8b9b64d", // "categories":["5ee0a7f22adf1624e8b9b64d","5ee0a7f22adf1624e8b9b64e"]}]}, // {"_id":"5ee0a7f22adf1624e8b9b64e","name":"vue.js","__v":0,"posts":[{"_id":"5ee0a74dc6a921238454fa51","title":"第1篇文章","body":"内容1","__v":5,"category":"5ee0a7f22adf1624e8b9b64d","categories":["5ee0a7f22adf1624e8b9b64e"]},{"_id":"5ee0a74dc6a921238454fa52","title":"第2篇文章","body":"内容2","__v":9,"category":"5ee0a7f22adf1624e8b9b64d","categories":["5ee0a7f22adf1624e8b9b64d","5ee0a7f22adf1624e8b9b64e"]}]}] // await Post.insertMany([{ // title: '第1篇文章', // body: '内容1' // }, { // title: '第2篇文章', // body: '内容2' // }]); // await Category.insertMany([{ // name: 'nodejs' // }, // { // name: 'vue.js' // } // ]); // const category = await Category.find(); // console.log(category); // const cat1 = await Category.findOne({ name: 'nodejs' }); // const cat2 = await Category.findOne({ name: 'vue.js' }); // const post1 = await Post.findOne({ title: '第1篇文章' }); // const post2 = await Post.findOne({ title: '第2篇文章' }); // posts.category // console.log(cat1); // post1.categories = [cat2]; // post2.categories = [cat1, cat2]; // await post1.save(); // await post2.save(); // const post = await Post.find(); // console.log(post); // console.log(post1, post2); // const post = await Post.find().populate('categories') // console.log(post[0], post[1]);}init()

June 10, 2020 · 2 min · jiezi

NodeJsExpress简单的用户注册登录和授权

前言:新建node-auth文件夹,新建server.js文件,初始化文件夹 npm init -y(git init)1.安装express, mongoose,rest-client2.开启服务器const express = require('express)const app = express()//连接数据库require('./modles/db')//jwtconst jwt = require('jsonwebtoken')app.use(express.json()) //密钥const SECRET = 'sajkFAjscbhsafchdsvjkks';app.get('/api', async(req, res) => { const user = await User.find(); res.send(user); // res.send('ok');})//注册app.post('/api/register', async(req, res) => { const user = await User.create({ username: req.body.username, password: req.body.password }) res.send(user); // console.log(req.body);});//登录app.post('/api/login', async(req, res) => { const user = await User.findOne({ username: req.body.username }); if (!user) { return res.status(422).send({ message: '用户名不存在' }) } //验证密码 compareSync const isPasswordValid = require('bcryptjs').compareSync(req.body.password, user.password); if (!isPasswordValid) { return res.status(422).send({ message: '密码错误' }); } // 生成token const token = jwt.sign({ id: String(user._id) }, SECRET) res.send({ user, token }) // res.send(isPasswordValid); // res.send(user);});// 中间件const auth = async(req, res, next) => {//获取token const raw = String(req.headers.authorization.split(' ').pop()); //解析 const { id } = jwt.verify(raw, SECRET); req.user = await User.findById(id); next()}// 个人信息app.get('/api/profile', auth, async(req, res) => { res.send(req.user);})app.listen(3000, () => {` console.log('listening port 3000!');})3.连接数据库 ...

June 10, 2020 · 2 min · jiezi

NodeJs与python的使用对比

写这篇文章的目的是想记录下NodeJs(后面简称node)与python的使用对比,希望看完之后大家对node跟python有个基本的认识。 (本文使用的node版本为v12.14.0,python为v3.8.3。) 简介node 是一个基于 Chrome V8 引擎的 JavaScript(简称js) 运行时。简单的说就是通过v8引擎(由c++编写)解释并执行js代码,然后就能运行在服务器上。python则是一门面向对象的解释型编程语言,目前最广泛的python解释器是CPython,就是通过C语言把python代码编译成字节码然后在虚拟机上运行。node适用于前端代码的打包发布、服务端开发跟桌面端应用开发等。而python则适合科学计算、数据分析、自动化运维等场景。 数据结构node的数组对应python的列表,都可以存放多种不同类型的数据。node对象则对应python的字典,都是使用key-value的形式。set结构也是类似的概念,都是没有重复元素的集合。node没有python中的元组类型,但是可以通过Object.freeze实现类似的效果。node let list = [1,2,3]list.push(4) // list [1,2,3,4]list.splice(2,1) // list [1,2,4]list.concat([5,6]) // [1,2,4,5,6]list.slice(1) // [2,4]let [a,b,c] = list // a=1 b=2 c=4let tuple = [1,2]Object.freeze(tuple)tuple[0] = 3 // tuple [1,2]//一般可以使用for、forEach、for...of进行遍历list.forEach((item)=>{console.log(item)}) //1 2 4python list = [1,2,3]list.append(4) # list [1,2,3,4]del list[2] # list [1,2,4]list + [5,6] # [1,2,4,5,6]list[1:] # [2,4]a,b,c = list # a=1 b=2 c=4tuple = (1,2)tuple[0] = 3 # 报错 tuple (1,2)#通过for in遍历for item in list: print(item) # 1 2 4变量与作用域nodenode中全局变量在global对象上定义,可以在多个模块中访问。模块中声明变量可以通过var、let和const,其中let跟const在代码块(if、for等)内无法被外面的方法访问,而var可以。除了块级作用域外,还有函数作用域,函数作用域内的变量想被函数外访问则需要通过闭包。另外每个js文件就是一个模块,而模块最终会被一个匿名函数包裹(exports跟module就是匿名函数里的参数),所以模块里的变量也是局部变量。 ...

June 9, 2020 · 2 min · jiezi

nodejsbuffer-基础篇

概念buffer存了什么buffer是一个操作字节的对象,它的底层是一个字节数组,存储着16进制数字。 var str = 'hello buffer'var buffer = new Buffer(str, 'utf-8')console.log(buffer) //输出的是十六进数字buffer的每个元素是16进制的两位数,也就是每个元素的大小是0-255. 因为 F X 16 + F X 16^0 = 255 溢出了怎么办我们可以直接对buffer的元素进行赋值 var buffer = new Buffer(10)buffer[0] = 300 console.log(buffer[0])如果赋的值是小数,小数部分会直接被舍弃。溢出的时候,就是如果大于255就减去256直到小于255,如果小于0则加上256直到大于0 if ( x > 255 ) { while(x > 255) { x = x - 256 } } if ( x < 0 ) { x = x + 256 }补充:~~~~ 负数在计算机里面存储的是补码,最高位为符号位(0为正,1为负),除符号位,其他位会取反,最低位取反后加1如:-1取反前: 1000 0001取反: 1111 1110末位加1:1111 1111这样计算机读出来就是255啦 ...

June 6, 2020 · 1 min · jiezi

基于Nodejs平台web框架Express-VS-Koa

Express和Koa都是基于Nodejs平台的web框架,也是目前比较常见的用于快速开发web服务的框架,且两者都是基于middleware的方式去处理客户端请求,那么两者有何区别呢?简单点说就是,“Express是直线型,Koa是洋葱模型”。(喊口号!!!)我们先来看看下面的示例代码: // for express exampleconst express = require('express');const app = express();function cb1(req, res, next) { console.log('>>>>>>cb1'); next(); console.log('<<<<<<cb1');}function cb2(req, res, next) { console.log('>>>cb2<<<'); res.send('hello world');}app.use('/', [cb1, cb2]);app.listen(3000);// for koa2 exampleconst koa = require('koa2');const app = koa();function cb1(ctx, next) { console.log('>>>>>>cb1'); next(); console.log('<<<<<<cb1');}function cb2(ctx, next) { console.log('>>>cb2<<<'); ctx.body = 'hello world';}app.use(cb1);app.use(cb2);app.listen(3000);以上两段代码的输出皆为: >>>>>>cb1>>>cb2<<<<<<<<<cb1所以,当middleware为同步函数时,两者从执行结果上来看并无区别。我们再来看看下面的示例代码: // for express exampleconst express = require('express');const app = express();async function cb1(req, res, next) { console.log('>>>>>>cb1'); await next(); console.log('<<<<<<cb1');}async function cb2(req, res, next) { return new Promise((resolve) => { setTimeout(resolve, 500); }).then(() => { console.log('>>>cb2<<<'); res.send('hello world'); });}app.use('/', [cb1, cb2]);app.listen(3000);// for koa2 exampleconst koa = require('koa2');const app = new koa();async function cb1(ctx, next) { console.log('>>>>>>cb1'); await next(); console.log('<<<<<<cb1');}async function cb2(ctx, next) { return new Promise((resolve) => { setTimeout(resolve, 500); }).then(() => { console.log('>>>cb2<<<'); ctx.body = 'hello world'; });}app.use(cb1);app.use(cb2);app.listen(3000);express-example的输出为: ...

June 6, 2020 · 3 min · jiezi

Nodejs基础

第一节:模块化跟Vue一样,单独创一个.js文件!运行方法首先你自己建一个文件夹比如叫test然后在顶部路径:如F:/test选中后输入cmd运行就能直接获取到路径,如果要运行起来就运行你自己要运行的文件名node ./app.js 主页面引入就是模块化他们的区别就是Vue: //B.jsconst router =[ ]export default router//A文件import router from './router'node: 这里提到的是如果B是封装对象就用module.exports = test如果是方法就用function test(){}exports.xxxx = test //B文件暴露出来const test ={ set(){ console.log(123) }}module.exports = test//A文件引入const test = require('./test')test.set()第二节:package.json默认的都是index,如果有人非要起自己的名!可以直接在你的文件目录输入 npm init --yes自己就生成了一个文件package.json,main这个你随意改吧,想怎么改就怎么改! { "name": "cnm", "version": "1.0.0", "description": "", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC"}第三节:第三方包安装这个安装很简单第一步:npm i md5 -S第二步:在app.js里面引入MD5第三步:在这里进行赋值即可 npm i md5 -Svar MD5 require("md5)console.log(MD5("haha438741"))第四节:fs模块说白了就是文件路径,你的数据改了怎么保存。保存到了哪里?这里有几个常用的 ...

June 6, 2020 · 1 min · jiezi

Nodejs-Express-Mongodb-基础

1--》快速上手路由const express = require("express"); const app = express(); app.get('/', (req, res) => { res.send({ success: 'ok'});}) app.listen(3000, () => { console.log("APP is listening 3000!");}) 2--》静态文件托管const path = require("path"); app.use(express.static(path.join(__dirname, 'public'))) 3--》CORS跨域请求1:npm i cors app.use(require('cors)()); 2: app.all('*', function(req, res, next) { res.header('Access-Control-Allow-Origin', '*');res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With');res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');next();}); 4--》MongoDB基础const mongoose = require("mongoose"); mongoose.connect('mongodb://localhost:27017/database', {useNewUrlParser: true,}); const testSchema = new mongoose.Schema({ title: String}) const Test = mongoose.model('Test',testSchema) ...

June 5, 2020 · 1 min · jiezi

Nodejs获取本机内网ip

通过引入os模块获取系统信息var os = require('os');var serverIP = getIPAddress();console.log('serverIP', serverIP);// 获取内网ipfunction getIPAddress() { let IPAddress = ''; var interfaces = os.networkInterfaces(); for (var devName in interfaces) { var iface = interfaces[devName]; for (var i = 0; i < iface.length; i++) { var alias = iface[i]; if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) { IPAddress = alias.address; } } } return IPAddress;};

June 5, 2020 · 1 min · jiezi

nodejssequelize扩展使用

安装Sequelizenpm i sequelize驱动安装(选择以下其一) npm install --save pg pg-hstore # Postgresnpm install --save mysql2npm install --save mariadbnpm install --save sqlite3npm install --save tedious # Microsoft SQL Server连接数据库测试连接初次体验导入sequelize包 创建sequelize实例(通过实例构造方法传入参数创建连接数据库地址) .authenticate()函数测试连接是否正常 const Sequelize = require('sequelize');const sequelize = new Sequelize('dog', 'root', '123456', { host: 'localhost', /* 选择 'mysql' | 'mariadb' | 'postgres' | 'mssql' 其一 */ dialect: 'mysql' });try { sequelize.authenticate(); console.log("ok");} catch (err) { console.log(err)}构造函数参数详解https://sequelize.org/master/... 关闭连接默认情况下,在保持连接打开的状态,并对所有的查询使用相同的连接 sequelize.close()模型模型模型的本质其实是代表的是数据库中表的抽象,包含数据库表的名称以及该名称下所具有的列(以及列的数据类型) 模型定义-define方式sequelize.define(modelName, attributes, options)函数详解https://sequelize.org/master/... ...

June 4, 2020 · 2 min · jiezi

实时日志监控

实时日志监控服务: log.io, 主要有两部分组成, 分别为 服务端 和 客户端.要求安装log.io时, node的版本应该为最新的稳定版,我的node版本为v12.17.0ref: centos7安装最新稳定版本的nodejs 服务端安装全局安装 log.io-servernpm install -g log.io添加默认配置文件# 添加目录sudo mkdir -p /root/.log.io# 添加配置文件(server.json的具体内容请参看下面给出的示例)vim /root/.log.io/server.json{ "messageServer": { "port": 6689, "host": "0.0.0.0" }, "httpServer": { "port": 6688, "host": "0.0.0.0" }, "debug": false, "basicAuth": { "realm": "abc123xyz", "users": { "登录用户名": "登录密码" } }}PM2启动服务端如果没有pm2服务, 请使用npm安装, 如: npm install -g pm2 pm2 start log.io-server如果想用pm2以配置文件方式启动, 参考文中底部的说明. 验证浏览器输入ip + 6688端口, 输入用户名和密码, 就可以进入实时日志界面 (当然, 需要看到日志,还需要往下配置客户端) http://localhost:6688 客户端安装全局安装log.io-file-inputnpm install -g log.io-file-input添加默认配置文件# 添加目录sudo mkdir -p /root/.log.io/inputs# 添加配置文件vim /root/.log.io/inputs/file.json{ "messageServer": { "host": "0.0.0.0", "port": 6689 }, "inputs": [ { "source": "服务器名称", "stream": "网关", "config": { "path": "/data/xxxx/gateway-0.0.6.log" } }, { "source": "服务器名称", "stream": "xxxx_user", "config": { "path": "/data/xxx/logs/consumer-user-test.log" } } ]}PM2启动服务端pm2 start log.io-file-input验证再次在浏览器中访问 6688 端口, 就可以看到效果了 ...

June 3, 2020 · 1 min · jiezi

JWT

什么是 JWTJSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案。是一种认证授权机制。JWT 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准(RFC 7519)。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上。可以使用 HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进行签名。因为数字签名的存在,这些传递的信息是可信的。阮一峰老师的 JSON Web Token 入门教程 讲的非常通俗易懂,这里就不再班门弄斧了生成 JWTjwt.io/www.jsonwebtoken.io/ JWT 的原理 JWT 认证流程:用户输入用户名/密码登录,服务端认证成功后,会返回给客户端一个 JWT客户端将 token 保存到本地(通常使用 localstorage,也可以使用 cookie)当用户希望访问一个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使用Bearer 模式添加 JWT,其内容看起来是下面这样Authorization: Bearer复制代码 服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为因为 JWT 是自包含的(内部包含了一些会话信息),因此减少了需要查询数据库的需要因为 JWT 并不使用 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)因为用户的状态不再存储在服务端的内存中,所以这是一种无状态的认证机制JWT 的使用方式客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。方式一当用户希望访问一个受保护的路由或者资源的时候,可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求头信息的 Authorization 字段里,使用 Bearer 模式添加 JWT。 GET /calendar/v1/events Host: api.example.com Authorization: Bearer <token>用户的状态不会存储在服务端的内存中,这是一种无状态的认证机制服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为。由于 JWT 是自包含的,因此减少了需要查询数据库的需要JWT 的这些特性使得我们可以完全依赖其无状态的特性提供数据 API 服务,甚至是创建一个下载流服务。因为 JWT 并不使用 Cookie ,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)方式二跨域的时候,可以把 JWT 放在 POST 请求的数据体里。方式三通过 URL 传输 http://www.example.com/user?token=xxx项目中使用 JWT**项目地址: https://github.com/yjdjiayou/... ** ...

June 1, 2020 · 1 min · jiezi

前端包管理工具的配置流程

前端包管理工具的配置流程一:新建目录结构,初始化项目1 新建一个文件夹作为新项目的工作空间。2 使用vscode工具打开文件夹,使用【npm init -y】命令初始化项目,项目初始化后出现一个【package.json】的文件。3 新建一个【src】目录,用来存放源代码。4 新建一个【dist】目录,用来存放产品的打包文件。5【src】下新建首页【index.html】。6【src】下新建入口文件【index.js】。 二:安装webpack的包管理工具7 安装webpack打包工具 【cnpm i webpack webpack-cli -D】8 全局运行【npm i cnpm -g】9 在【webpack.config.js】配置文件中配置webpack //向外暴露一个打包对象module.exports = { mode:'development'//development production}10 约定的打包的入口文件为【index.js】文件,【约定大于配置的规则】 11 使用【webpack】打包,打包后在【dist】目录下生成一个【main.js】的文件。 三:配置修改后自动编译:12 实时打包编译工具:【webpack-dev-server】 13 安装:【cnpm i webpack-dev-server -D】 14 在【package.json中配置】 "dev":"webpack-dev-server"15 执行:【npm run dev】,即可完成修改代码后的自动编译 16 生成的【mian.js】是放在内存中的根目录下,引用内存中的【main.js】 四:配置编译后自动打开浏览器17 "dev":"webpack-dev-server --open"五:配置编译后自动跳转到浏览器后自动打开首页1 问题:编译后没有自动跳转到首页 2 解决:配置编译后自动跳转到首页,即配置首页到内存中 3 安装【html-webpack-plugin】插件,【cnpm i html-webpack-plugin -D】 4 在【webpack.config.js】中进行配置【html-webpack-plugin】插件 //配置插件 const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') //配置在内存中自动生成index.js的插件 //创建一个插件的实例对象 const htmlPlugin = new HtmlWebpackPlugin({ template:path.join(__dirname,'./src/index.html'), filename:'index.html' })//向外暴露一个打包对象module.exports = { mode:'development', plugins:[ htmlPlugin ]}六:总结:总结:(1)完成了打包后的【mian.js】文件进内存(2)【index.html】进内存(3)并且打包好的【mian.js】自动注入到【index.html】中,使用包管理工具的基本环境设置完成。 ...

June 1, 2020 · 1 min · jiezi

Java爬虫使用JvppeteerPuppeteer轻松爬淘宝商品

Java爬虫:使用Jvppeteer(Puppeteer)轻松爬淘宝商品想要爬取某宝的商品,如果只是用HttpURLConnection发个请求,失败率是很高的。一般想要保证成功率的话,都会选择真实的浏览器去抓取。 以前常用的解决方案是selenium或phantomjs,但是它两的环境配置太麻烦了,对程序员极度不友好,自从谷歌推出Puppeteer后,puppeteer迅速流行起来,获得大家一致称赞。它是一个NodeJS库,但今天并不是要使用它来爬取某宝商品,而是使用Java语言写的Jvppeteer,Jvppeteer与Puppeteer是同样的实现原理。 思路使用多线程,一个线程负责一个页面的爬取(接下来的内容会使用page代替页面)创建与线程池线程数相同的page队列,page放在LinkedBlockingQueue队列里,每当有爬取任务时,就从队列里取出一个page,爬取任务完成时,将page放回队列的后面。这样做的原因是重复利用page,减少页面的创建频率,但是要注意的是一个页面不能利用太久或者次数太多,防止出现crash的情况拦截图片和多媒体资源的加载,多媒体资源和图片的加载会极大影响页面的加载速度,从而影响爬虫效率,所以要拦截(可选)。我们选择获取整个页面内容,然后解析得到商品信息 代码实现1.启动浏览器 //指定启动路径,启动浏览器 String path = new String("F:\\java教程\\49期\\vuejs\\puppeteer\\.local-chromium\\win64-722234\\chrome-win\\chrome.exe".getBytes(), "UTF-8"); ArrayList<String> argList = new ArrayList<>(); LaunchOptions options = new OptionsBuilder().withArgs(argList).withHeadless(false).withExecutablePath(path).build(); argList.add("--no-sandbox"); argList.add("--disable-setuid-sandbox"); Browser browser = Puppeteer.launch(options);2.创建page队列与线程池//启动一个线程池多线程抓取 int threadCount = 5; ThreadPoolExecutor executor = new ThreadPoolExecutor(threadCount, threadCount, 30, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); CompletionService service = new ExecutorCompletionService(executor); //打开5个页面同时抓取,这些页面可以多次利用,这样减少创建网页带来的性能消耗 LinkedBlockingQueue<Page> pages = new LinkedBlockingQueue<>(); for (int i = 0; i < threadCount; i++) { Page page = browser.newPage(); //拦截请求,可选,但是存在线程切换比较严重,建议不拦截// page.onRequest(request -> {// if ("image".equals(request.resourceType()) || "media".equals(request.resourceType())) {// //遇到多媒体或者图片资源请求,拒绝,加载页面加载// request.abort();// } else {//其他资源放行// request.continueRequest();// }// });// page.setRequestInterception(true); pages.put(page);//往队列后面放,阻塞 }3.定义爬虫线程静态内部类static class CrawlerCallable implements Callable<Object> { private LinkedBlockingQueue<Page> pages; public CrawlerCallable(LinkedBlockingQueue<Page> pages) { this.pages = pages; } @Override public Object call() { Page page = null; try { page = pages.take(); PageNavigateOptions navigateOptions = new PageNavigateOptions(); //如果不设置 domcontentloaded 算页面导航完成的话,那么goTo方法会超时,因为图片请求被拦截了,页面不会达到loaded阶段 navigateOptions.setWaitUntil(Arrays.asList("domcontentloaded")); page.goTo("https://item.taobao.com/item.htm?id=541605195654", navigateOptions); String content = page.content(); return parseItem(content); } catch (Exception e) { e.printStackTrace(); } finally { if (page != null) { try { pages.put(page);//把已经抓取完的网页放回队列里 } catch (InterruptedException e) { e.printStackTrace(); } } } return null; } }4.解析商品,获取结果//结果集 List<Future<Object>> futures = new ArrayList<>(); //抓取100次 long start = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { Future<Object> future = service.submit(new CrawlerCallable(pages)); futures.add(future); } //关闭线程池 executor.shutdown(); //获取结果 int i = 0; for (Future<Object> result : futures) { Object item = result.get(); i++; System.out.println(i + ":" + Constant.OBJECTMAPPER.writeValueAsString(item)); } long end = System.currentTimeMillis(); System.out.println("时间:" + (end - start));经过测试,爬取的速度非常之快,100个任务用了15s就完成了,但不同的电脑配置和带宽有不同结果,因为爬虫是比较吃配置还有带宽的。 ...

May 30, 2020 · 2 min · jiezi

nodejs-包管理器安装

src: Github Installation instructionsNode.js v14.x: Using Ubuntucurl -sL https://deb.nodesource.com/setup\_14.x | sudo -E bash -sudo apt-get install -y nodejsUsing Debian, as rootcurl -sL https://deb.nodesource.com/setup\_14.x | bash -apt-get install -y nodejsNode.js v13.x: Using Ubuntucurl -sL https://deb.nodesource.com/setup\_13.x | sudo -E bash -sudo apt-get install -y nodejsUsing Debian, as rootcurl -sL https://deb.nodesource.com/setup\_13.x | bash -apt-get install -y nodejsNode.js v12.x: Using Ubuntucurl -sL https://deb.nodesource.com/setup\_12.x | sudo -E bash -sudo apt-get install -y nodejsUsing Debian, as rootcurl -sL https://deb.nodesource.com/setup\_12.x | bash -apt-get install -y nodejsNode.js v10.x: ...

May 30, 2020 · 1 min · jiezi

Segmentation-fault-node报错

报错信息 (dv) root@JoJo:~# node -vSegmentation fault(dv) root@JoJo:~# npm -vSegmentation fault使用n管理,在node版本从9.2.0回退到8.7.0的时候,可能是在二进制文件下载完成,然后node命令和npm命令执行都会返回 Segmentation fault 在Stack Overflow查了一圈,尝试把nodejs卸载掉,但无效 apt-get remove --purge nodejs解决使用n安装另一个版本, n 14.0.0查看所有版本: sudo n移除有问题的版本: n rm 8.7.0n 选择要使用的版本。 root@JoJo:~# node -vv8.7.0

May 30, 2020 · 1 min · jiezi

egg多进程模型注意事项梳理

背景最近在项目中使用egg进行服务端开发,在开发过程中遇到了比较诡异的问题,具体表现为mq在监听到信息时,其回调函数会被多次执行,那么这会导致某个文件被同时操作等问题。 问题成因这边梳理egg文档时,重点过了一下egg多进程的设计模式,了解到egg的master-agent-worker模式,那么这里面有些问题是需要我们在开发时去注意的了。 首先介绍下egg的多进程实现方式egg通过node提供的cluster实现了多进程模式,为了更好地利用多核环境,egg一般会启用相当于cpu核数的worker,以此来最大化利用cpu能力。 在egg启动时,master,agent,worker的关系如图所示 +---------+ +---------+ +---------+| Master | | Agent | | Worker |+---------+ +----+----+ +----+----+ | fork agent | | +-------------------->| | | agent ready | | |<--------------------+ | | | fork worker | +----------------------------------------->| | worker ready | | |<-----------------------------------------+ | Egg ready | | +-------------------->| | | Egg ready | | +----------------------------------------->|在这种模式下,master、agent、worker各司其职,主要制作分配如下:master:负责维护整个应用稳定性,当有worker因异常而退出时,master负责拉起新的worker,以确保应用正常运行。agent:由于egg的多进程模型会在每个进程中运行一份我们的应用实例,那么在某些情况下,这种机制会导致问题。比如,保存日志的逻辑如果在每个进程中都执行的话,那么在触发日志保存操作的时候,会有多个进程同时操作日志文件,那么此时就会导致文件读写问题。所以egg设计了agent进程,agent进程只会有一个,不会出现上述问题,这样,对于类似上述的后台运行的逻辑就统一放到agent中去处理了。worker:负责执行业务代码,处理用户请求和定时任务,egg在框架层保证了定时任务只会在单个worker中执行,所以可以放心使用。 分析egg多进程导致的问题上面我们分析过了egg的多进程机制,所以我们知道了问题成因,出现我们最开始说的问题的原因是我们把mq的监听和处理逻辑放到了worker中,那么这样的话在实际运行过程中,就会导致mq收到消息时,回调函数被执行多次。 到这里我们已经知道如何优化了,那就是把mq的处理逻辑放到agent中,以确保mq消息的回调仅执行一次。但是细心地你肯定发现了,这里有个问题,agent只有一个实例,如果事情在agent里面做,那么不是无法利用多核性能了吗?agent与worker通信的确,我们可以在agent中处理仅需要单次执行的逻辑,但是这样做就没法利用多核性能了。那么有什么办法吗?没错,就是进程间通信,具体思路就是,agent还是负责mq的连接和监听逻辑,但是回调函数不在agent中执行,而是写在worker里面。那么worker什么时候执行这个逻辑呢?答案是,agent通过进程间通信通知worker。egg内部实现了一个进程间通信机制,我们直接调用即可,主要实现方式如下: 广播消息: agent => all workers +--------+ +-------+ | Master |<---------| Agent | +--------+ +-------+ / | \ / | \ / | \ / | \ v v v +----------+ +----------+ +----------+ | Worker 1 | | Worker 2 | | Worker 3 | +----------+ +----------+ +----------+指定接收方: one worker => another worker +--------+ +-------+ | Master |----------| Agent | +--------+ +-------+ ^ | send to / | worker 2 / | / | / v +----------+ +----------+ +----------+ | Worker 1 | | Worker 2 | | Worker 3 | +----------+ +----------+ +----------+这里我们可以看出来,进程间通信都是基于master转发的,所以我们可以利用egg提供的机制,解决我们的问题。 ...

May 29, 2020 · 1 min · jiezi

Deno来了Node要凉了吗

背景故事最近Deno讨论比较热门,并且有说 “Deno很可能是下一个前端的大事件”,所以关注下。 Node.js 和 Deno 的起源作者都是Ryan Dahl他从2009年开始从事Node项目,但是几年后却退出了该项目。在2018年的时候,他发表了我对Node.js感到遗憾的10件事,另外他宣布了创建全新的Deno项目 。 作者GitHub https://github.com/ry Ryan DahI 提到的Node十个设计错误,很多都是基层方面的设计错误,要深入理解需要有专业功底。本人作为小白,只是整理罗列。 后悔 没有坚持使用Promise 的结果是导致Node里面充满了async / await和promise的不同async API设计,直到现时都极难整合。 后悔 没有从GYP加固系统转到GN后悔 继续使用GYP,没有提供FFI后悔 在任何地方也可以require(“ somemodule”)后悔 package.json提供了错误的“ module”观念后悔 没有注重安全性(Security)后悔 设计了软件界黑洞node_modules 有的时候 npm install 要等很久,然后发现应用下载了几百mb的node_module。 附上演讲视频地址:https://www.bilibili.com/vide...DenoDeno是使用JavaScript和TypeScript编写应用程序的新平台。两种平台具有相同的理念-事件驱动架构和异步非阻塞工具来构建Web服务器和服务。 Node 和 Deno 有何不同?这两个平台具有相同的目的,但是使用不同的机制。Deno使用ES模块作为默认模块系统,而Node.js使用CommonJS。外部依赖项是使用URL加载的,类似于浏览器。也没有包管理器和集中式注册表,可以在Internet上的任何位置托管模块。与Node.js相反,Deno在沙箱中执行代码,这意味着运行时无法访问网络,文件系统和环境。需要明确授予访问权限,这意味着更好的安全性。Deno开箱即用地支持TypeScript,这意味着我们不需要手动安装和配置工具来编写TypeScript代码。另一个区别是Deno提供了一组内置工具,例如测试运行器,代码格式化程序和捆绑程序。 Deno不需要npm包管理# Denoimport { serve } from "https://deno.land/std@0.53.0/http/server.ts";# Node const server requrie('server')Deno通过URL导入代码,可以在互联网上的任何地方托管模块。无需集中注册表即可分发Deno软件包。也不需要package.json文件和依赖项列表,因为所有模块都是在应用程序运行时下载,编译和缓存的。 Deno 真的会取代node?Krzysztof Piechowicz:Deno的目标不是取代Node.js,而是提供替代方案。其中一些差异颇具争议,很难预测它们是否将以正确的方式格式化。我建议所有Node.js程序员都注意这个项目。我不确定该项目是否会成功,但这是观察Node.js如何以不同方式实现的绝佳机会。演讲视频地址:https://www.bilibili.com/vide...尝试一下附录资源 官网:https://deno.land源码:https://github.com/denoland/deno # 安装curl -fsSL https://deno.land/x/install/install.sh | sh# 运行Demodeno run https://deno.land/std/examples/welcome.ts例子 import { serve } from "https://deno.land/std@0.53.0/http/server.ts";const s = serve({ port: 8000 });console.log("http://localhost:8000/");for await (const req of s) { req.respond({ body: "Hello World\n" });}总结Deno 是否会带来变革,我们拭目以待!! ...

May 27, 2020 · 1 min · jiezi

node进程间通信

作为一名合格的程序猿/媛,对于进程、线程还是有必要了解一点的,本文将从下面几个方向进行梳理,尽量做到知其然并知其所以然: 进程和线程的概念和关系进程演进进程间通信理解底层基础,助力上层应用进程保护进程和线程的概念和关系用户下达运行程序的命令后,就会产生进程。同一程序可产生多个进程(一对多关系),以允许同时有多位用户运行同一程序,却不会相冲突。进程需要一些资源才能完成工作,如CPU使用时间、存储器、文件以及I/O设备,且为依序逐一进行,也就是每个CPU核心任何时间内仅能运行一项进程。 进程与线程的区别:进程是计算机管理运行程序的一种方式,一个进程下可包含一个或者多个线程。线程可以理解为子进程。 摘自wiki百科 也就是说,进程是我们运行的程序代码和占用的资源总和,线程是进程的最小执行单位,当然也支持并发。可以说是把问题细化,分成一个个更小的问题,进而得以解决。 并且进程内的线程是共享进程资源的,处于同一地址空间,所以切换和通信相对成本小,而进程可以理解为没有公共的包裹容器。 但是如果进程间需要通信的话,也需要一个公共环境或者一个媒介,这个就是操作系统。 进程演进我们的计算机有单核的、多核的,也有多种的组合方式: 单进程因为是一个进程,所以某一时刻只能处理一个事务,后续需要等待,体验不好 多进程为了解决上面的问题,但是如果有很多请求的话,会产生很多进程,开销本身就是一个不小的问题,而进程占据独立的内存,这么多响应是的进程难免会有重复的状态和数据,会造成资源浪费。 多进程多线程由之前的进程处理事务,改成使用线程处理事务,解决了开销大,资源浪费的问题,还可以使用线程池,预先创建就绪线程,减少创建和销毁线程的开销。 但是一个cpu某一时刻只能处理一个事务。像时间分片来调度线程的话,会导致线程切换频繁,是非常耗时的。 单进程单线程类似也就是v8,基于事件驱动,有效的避免了内存开销和上下文切换,只需要线程间通信,即可在适当的时刻进行事务结果等的反馈。 但是遇到计算量很大的事务,会阻塞后续任务的执行。像这样: 单进程单线程(多进程架构)node提供了cluster和child_process两个模块进行进程的创建,也就是我们常说的主(Master)从(Worker)模式。Master负责任务调度和管理Worker进程,Worker进行事务处理。 进程间通信node本身提供了cluster和child_process模块创建子进程,本质上cluster.fork()是child_process.fork()的上层实现,cluster带来的好处是可以监听共享端口,否则建议使用child_process。 child_processchild_process提供了异步和同步的操作方法,具体可查看文档。 常见的异步方法有: .exec.execFile.fork.spawn除了fork出来的进程会长期驻存外,其他方式会在子进程任务完成后以流的方式返回并销毁进程。 异步方法会返回ChildProcess的实例,ChildProcess不能直接创建,只能返回。 来看几张图吧: 举个例子有一个很长很长的循环,如果不开启子进程,会等循环之后才能执行之后的逻辑。 我们可以将耗时的循环放到子进程中,主进程会接受子进程的返回,不影响后续事物的处理。 // 主进程const execFile = require('child_process').execFile;execFile('./child.js', [], (err, stdout, stderr) => { if (err) { console.log(err); return; } console.log(`stdout: ${stdout}`);});console.log('用户事务处理');// 子进程#!/usr/bin/env nodefor (let i = 0; i < 10000; i++) { process.stdout.write(`${i}`);}而对于fork,它是专门用来生产子进程的,也可以说是主进程的拷贝,返回的ChildProcess中会内置额外的通信通道,也就是IPC通道,允许消息在父子进程间传递,例如通过文件描述符,不过由于创建的是匿名通道,所以只有主进程可以与之通信,其他进程无法进行通信。但相对的还有命名通道,详见下一节。 看一个简单的例子: //parent.jsconst cp = require('child_process');const n = cp.fork(`${__dirname}/sub.js`);n.on('message', (m) => { console.log('PARENT got message:', m);});n.send({ hello: 'world' });//sub.jsprocess.on('message', (m) => { console.log('CHILD got message:', m);});process.send({ foo: 'bar' });父进程通过fork返回的ChildProcess进行通信的监听和发送,子进程通过全局变量process进行监听和发送。 ...

May 26, 2020 · 1 min · jiezi

流批一体机器学习算法平台

针对正在兴起的机器学习广泛而多样的应用场景,阿里巴巴计算平台基础算法团队在2017年开始基于Flink研发新一代的机器学习算法平台。该项目名称定为Alink,取自相关名称(Alibaba, Algorithm, AI, Flink, Blink)的公共部分。经过三年的投入研发,Alink在算法性能、算法规模、算法易用性等方面取得了不错的成果,并实现了产品化。这使得数据分析和应用开发人员能够轻松搭建端到端的业务流程。 在后面的篇幅中,我们将从算法功能、算法性能、用户界面、可视化等方面对Alink做一个系统的介绍。 算法功能Alink拥有丰富的批式算法和流式算法,能够帮助数据分析和应用开发人员能够从数据处理、特征工程、模型训练、预测,端到端地完成整个流程。如下图所示,Alink提供的功能算法模块中,每一个模块都包含流式和批式算法。比如线性回归,包含批式线性回归训练,流式线性回归预测和批式线性回归预测。另外,Alink算法覆盖分类、回归、聚类、评估、统计分析、特征工程、异常检测、文本、在线学习、关联分析等经典领域,是一个通用的机器学习算法平台。 目前,Alink已经被阿里巴巴集团内部多个BU使用,并取得了不错的业务提升。特别是在2019年天猫双11中,单日数据处理量达到 970PB,每秒处理峰值数据高达 25 亿条。Alink 成功经受住了超大规模实时数据训练的检验,并帮助天猫产品推荐的点击率提高了4%。 算法性能下图给出的是一些经典算法与Spark的性能对比,通过该图可以看出,Alink在大部分算法性能优于Spark,个别算法性能比Spark弱,整体是一个相当的水平。 但是,“在功能的完备性方面,Alink更有优势”,Alink除了覆盖Spark的算法,还包含流式算法、流批混跑、在线学习、中文分词等。 用户使用界面为了提供更好的交互式体验,我们提供两种用户使用界面:web和PyAlink。 首先我们介绍一下web界面。Web界面提供拖拽的方式创建试验,通过对每一个组件进行配置完成整个试验的参数配置。下图给出的是web界面创建的批式、流式、流批混合的试验。 并且Alink可以支持节点的级别实验运行状态显示。在各个算法节点旁,我们用闪烁的小灯泡表示“运行中”的状态,用对勾表示“运行完成”的状态。一般情况下,只有批式(batch)组件才有可能运行结束。基于各个组件的运行状态,可以十分方便地判断当前实验运行到了什么程度。并且,如果实验运行中出现了报错或者长时间不结束的情况,也能根据组件运行状态更加方便地定位潜在出问题的组件。除了简单的运行状态以外,Alink还提供了查看组件输入、输出数据量指标的功能。对于不同类型的组件,Alink提供了不同的指标展现方式:对于流式(stream)组件来说,在组件运行时,可以接近实时地看到组件的输出BPS和RPS数值。而对于批式(batch)组件,在组件运行完成后,会展示总的输出数据条数和字节数。这些指标的展示对于判断实验/业务是否正常运行可以提供很多的参考,尤其对于一些线上实时的业务,通过这些指标就能直观地看到是否正常运行。 下面我们继续介绍PyAlink。为了满足脚本用户的需求,我们提供了PyAlink on notebook,用户可以通过PyAlink的python包使用Alink。PyAlink支持单机运行,也支持集群提交。并且打通Operator(Alink算子)和DataFrame的接口,从而使得Alink整个算法流程无缝融入python。PyAlink也提供使用Python函数来调用UDF或者UDTF。PyAlink在notebook中使用如下图,展示了一个模型训练预测,并打印出预测结果的过程: 可视化Alink中的可视化包括统计相关的可视化、模型类可视化以及评估可视化等,当前能进行大屏可视化的组件包括:统计分析类组件,直接展示的统计算法的结果;机器学习模型类组件,展示训练好的模型的信息;评估类组件,展示评估接口。下图给出的是统计可视化,通过下图可以看到我们的统计可视化支持窗口统计和累计统计,并且支持曲线、柱状图、统计表、矩阵图等多种展示方式。 同样,下面两幅图给出的是模型的可视化和评估的可视化。 总结展望经过三年的发展,Alink已经成为一个功能完备的机器学习算法平台,而且已经在2019年FFA19将代码开源到社区,让更多的人能够使用这个平台解决业务问题。虽然Alink开源已经取得了阶段性成果,但是我们将继续积极向FlinkML贡献代码,我们希望将更多优秀的机器学习算法贡献给Flink项目,也希望和社区一起努力,共同促进Flink社区机器学习生态的发展和繁荣。 上云就看云栖号:更多云资讯,上云案例,最佳实践,产品入门,访问:https://yqh.aliyun.com/本文为阿里云原创内容,未经允许不得转载

May 26, 2020 · 1 min · jiezi

nodejs入门二数据交互之GET请求

打印 req.url,查看请求urlreq.url 可以看到我们GET请求的链接以及数据 我们新建一个html,写一个简单的form表单,用get方式提交请求 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <form action="http://127.0.0.1:8081/aaa" method="get"> 用户:<input type="text" name="username" /> <br> 密码:<input type="password" name="password" /> <br> <input type="submit" value="提交"> </form></body></html>打开html文件,输入账号密码,在服务端打印 req.url 数据,请看图1.1。 // 4 .jsconst http = require('http')let server = http.createServer(function(req, res){ console.log(req.url) // 打印请求 url ->/aaa?username=123&password=qweqwe})server.listen(8081)【图1.1】 使用 querystring 模块处理url数据知识点:quertstring 我们可以看到,服务端接收到的url数据是 /aaa?username=123&password=qweqwe,"?"号后面就是我们再form表单中输入的数据,我门可以提取问号后面的字符串,然后用 querystring.parse() 将字符串处理成对象。 const http = require('http')const querystring = require('querystring') let server = http.createServer(function(req, res){ let [url, query] = req.url.split('?'); // url->'/aaa', query->'username=123&password=qweqwe' let get = querystring.parse(query); // { username: '123', password: 'qweqwe' }})server.listen(8081)使用 url 模块处理url数据知识点:url.parse()通过url.parse(req.url, true)会直接帮我处理url,第二个参数设置为true,会帮我们将form提交的数据转化成对象,请看图1.2。 ...

May 26, 2020 · 1 min · jiezi

Copy攻城狮日志Node快速重命名文件告别Potplay字幕困扰问题

↑开局一张图,故事全靠编↑ 前言Copy攻城狮日志的惯例,开局一张图,开始为您讲述一个鲜为人知的故事。故事的开头要从本大狮从盗版网站下载udemy课程的犯罪伊始说起,去年的某月某天,我真正接触到了“你的大学”udemy,本来想好好学学nodejs,结果发现Max的课199刀,因为恰好遇到没打折,然后一路google,结果到了某个bt网站找到了相应的种子。埋头一想,先开他个某雷会员下一波再说。别说,公司的网还是比较炸天的,17G的文件没多会就下载完毕了,用老司机工具PotPlay把玩一下,结果全都是英文字幕。然后“从入门到放弃”,一放就是大几个月…… My English is poor当专业的您看到这个标题的时候,您就能猜到我的“英国历史”水平,对的,另一段鲜为人知的故事是我的确没有拿到四级证书,这不得不提到我荒废的大学时代,现在依旧为过去的不努力而偿债。大学一开始我有想过好好学英语的,可能因为英语老师深深地吸引了我,甚至还加了wx,后来就不了了之了,哪怕是一句“hi”都不曾发过火。再后来就是每年都考四级,每年都挂,除了宿舍那考研的哥们在女友的“威逼利诱”之下在最后一学期终于顺利通过并考研成功。而我,和English却越走越远,直到工作中不得不去认识记忆那些乏味的API名字、专有名词……说回来udemy,个人感觉上面的课还是比较给力的,看过一些入门课,里面的讲师个个都是人才,说话又好听,带来的知识理念都比较新。所以我对下载的这个学习资料还是比较满意的,除了英文字幕看不太懂直到慢慢放弃。 PotPlay字幕及翻译Potplay是一款老司机软件,这里就不多表。16年的时候第一次使用就被深深的迷住了,不仅可以正规学习还可以特殊学习,一度导致营养更不上。后来装了左边的18年版本或就再也没有更新过,直到最近才知道新版加了字幕在线翻译功能。通过学习一些博文,似乎掌握了怎么看懂片中的情节--实时翻译字幕。凑巧的是下载的学习资料刚好有外挂字幕,符合字幕在线翻译的基本要求,不带字幕的视频+外挂字幕+在线翻译这样的结构是比较符合字幕在线翻译的。操作也十分简单,我这里用的“被泼冷水的”某度AI,先试用的标准版,目前使用体验尚佳。发达的大佬可以去撸个google云300刀的券直接对接google翻译。具体翻译接入可以直接上手把玩探索.百度翻译接入可参考:PotPlayer_Subtitle_Translate_Baidu翻译API配置(估计先要注册对应平台账号并开通服务): *-en.srt翻车其实一直有个问题在困扰我,同样的文件,旧版播放能正常显示字幕,新版的就无法正常显示字幕。通过细致的对比,发现旧版播放器能正常读取*-en.srt字幕文件,而新版播放器不能且认为是另外视频文件的字幕,当我把字幕文件文件名中的-en去掉之后发现新版播放器也能正常显示字幕并且还自动翻译了。至于是不是bug,我也没打算去深究。问题很明确,因为字幕文件名和视频文件名不匹配,导致播放器无法自动识别。解决问题的其中一个途径是复制并重命名所有的字幕文件,将文件名中最后的“-en”替换为空字符。既然问题和解决方法都有了,那么是时候表演真正的Copy技术了…… Node批量处理文件名对面的大佬“人生苦短,他用Python”,而我“万寿无疆,我用NodeJS”。直接Copy代码: const fs = require("fs");const path = require("path");const util = require("util");const readdir = util.promisify(fs.readdir);const stat = util.promisify(fs.stat);const timeStart = new Date();const filePath = path.resolve("./");function readDirRecur(file, callback) { return readdir(file).then((files) => { files = files.map((item) => { let fullPath = file + "/" + item; return stat(fullPath).then((stats) => { if (stats.isDirectory()) { return readDirRecur(fullPath, callback); } else { /*not use ignore files*/ if (item[0] == ".") { //console.log(item + ' is a hide file.'); } else { callback && callback(fullPath); } } }); }); return Promise.all(files); });}readDirRecur(filePath, function (filePath) { // 只处理.srt文件 将-en.srt处理为.srt if (path.extname(filePath) === ".srt") { let newPath = filePath.replace(/(.*)-en/, "$1"); if (fs.existsSync(newPath)) { console.log("该路径已存在"); // fs.unlinkSync(newPath) } else { fs.copyFileSync(filePath, newPath); } }}) .then(function () { console.log("done", new Date() - timeStart); //done 3.3 }) .catch(function (err) { console.log(err); });处理结果(还算比较理想):随机打开一个视频都能正常显示双语字幕了: ...

May 26, 2020 · 1 min · jiezi

nodejs入门一简单的服务和文件请求

hello world!知识点:response.write(), response.end() const http = require('http')let server = http.createServer(function(request, response){ response.write('hello world!'); // 把内容发个客户端(浏览器) response.end() // 结束请求,断开链接,不写end浏览器会以为数据没发完,一直转圈})server.listen(8080)如何从文件中读写数据知识点: fs模块,fs.writeFile(path, data, callback), fs.readFile(path, callback) const fs = require('fs'); fs.writeFile('./a.txt', '学习fs模块', function(err){ if(err){ console.log('写入失败') }else{ console.log('写入完成') }})fs.readFile('./a.txt', function(err, data){ if(err){ console.log(err) }else{ console.log(data) // console.log(data.toString()) // 如果只是文本,可以用同toString()方法 }})node XX.js运行文件,提示写入完成,会发现我们的目录下出现了 a.txt 文件。 读文件成功后会输出 Buffer 数据,Buffer数据是原始的二进制数据,因为nodejs出了处理文字数据外,也会处理文件、图片数据等,随便的转成字符串有的数据就毁了。 buffer 数据转换成字符串是给人看的,机器不需要看,直接返回给客户端可以直接显示请求文件知识点:req.url, res.writeHeader const http = require('http')const fs = require('fs')let server = http.createServer(function(req, res){ console.log(req.url) // 查看请求的url fs.readFile(`files@{req.url}`, (err, data)=>{ if(err){ res.writeHeader(404) res.write('Not Found!') res.end(); }else{ res.write(data); // 注意: 这里没有转换成字符串,浏览器可以直接显示。 res.end(); } })})server.listen(8080)我们新建文件夹 files, 新建一个1.html文件,写入一些内容。运行node服务后,我们请求 127.0.0.1:8080/1.html, 可以看到我们可以访问文件了。图:【新建文件夹 files】 ...

May 25, 2020 · 1 min · jiezi

nodejs入门目录

nodejs入门(一):简单的服务和文件请求

May 25, 2020 · 1 min · jiezi