首页 文章 精选 留言 我的

精选列表

搜索[nodejs],共1637篇文章
优秀的个人博客,低调大师

NodeJS 后端开发 - 使用 sequelize/cli 实现 mysql 数据迁移

文章目录 背景 实现 安装 sequelize/cli 依赖 初始化 实现数据迁移 参考 背景 在做后台接口开发时,我们总会遇到新增字段或者修改字段的问题。 在开发阶段我们可以删表重建,但是上线之后可不能这么搞了。所以数据迁移就很重要了。 所谓的数据迁移原理其实就是将数据复制出来然后再把表删了重新建表同时把数据再复制进去。 这里我用到的ORM是Sequelize所以下面简单介绍一下Sequelize实现数据迁移的过程。 实现 安装 sequelize/cli 依赖 npm install --save-dev sequelize-cli 初始化 在项目目录(我这里是starter)执行下面的命令 npx sequelize-cli init 会在当前目录下生成相关目录如下: config/config.json:包含配置文件,它告诉CLI如何连接数据库。可直接从项目的数据库配置中复制过来,内容如下: { "development": { "username": "root", "password": "数据库密码", "database": "lin-cms", "host": "localhost", "dialect": "mysql" }, "test": { "username": "root", "password": "数据库密码", "database": "lin-cms", "host": "localhost", "dialect": "mysql" }, "production": { "username": "root", "password": "数据库密码", "database": "lin-cms", "host": "localhost", "dialect": "mysql" } } migrations:包含所有迁移文件。数据迁移的主要文件,后面介绍。 seeders:包含所有种子文件。使用样本数据或测试数据填充数据库表时可以使用seeders文件去实现。比如向用户表中插入用户: module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.bulkInsert('Users', [{ firstName: 'John', lastName: 'Doe', email: 'example@example.com', createdAt: new Date(), updatedAt: new Date() }]); }, down: (queryInterface, Sequelize) => { return queryInterface.bulkDelete('Users', null, { }); } }; models:包含你的项目的所有模型。 实现数据迁移 目的是要在已经存在的 goods表中增加pay_type字段,并且不删除表中的数据。 我们先按照上面的步骤完成初始化之后,就可以创建迁移文件了。 可通过如下命令创建文件: npx sequelize-cli migration:generate --name migration-good-add-paytype-column migration-good-add-paytype-column:是我们的迁移文件名称 执行完之后会在migrations文件夹下生成文件: 接下来就是在该文件中编写迁移逻辑了,文件内容如下: 'use strict'; module.exports = { up: async (queryInterface, Sequelize) => { /** * Add altering commands here. 更新的操作 * * Example: * await queryInterface.createTable('users', { id: Sequelize.INTEGER }); */ return queryInterface.sequelize.transaction(t => { return Promise.all([ // 在 goods 表中增加 string 类型的 pay_type 字段 queryInterface.addColumn('Goods', 'pay_type', { type: Sequelize.DataTypes.STRING }, { transaction: t }), ]); }); }, down: async (queryInterface, Sequelize) => { /** * Add reverting commands here. 回退的操作 * * Example: * await queryInterface.dropTable('users'); */ } }; 编写完迁移逻辑之后,执行如下命令即可完成数据迁移: npx sequelize-cli db:migrate 最后我们可以在数据库表中看到已经在现有数据的基础上增加了pay_type字段。 多表依赖可参考文档中的介绍。 Sequelize 高级专题 TIP: 在新增完字段之后,应该还需要在原有的Model对象里面添加,如这里是model/good.js需要增加pay_type否则添加商品信息该字段没有添加成功。(目前测试是这样) import sequelize from '../lib/db'; import { Sequelize } from 'sequelize'; import { GoodParentCategory } from './good-category'; // 这是我们用于以下示例的模型的设置 const Good = sequelize.define('good', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, title: { type: Sequelize.STRING(50), allowNull: false }, // 新增 brand:{ type: Sequelize.STRING(50), allowNull: false }, image: { type: Sequelize.STRING(100), allowNull: false }, category: { type: Sequelize.STRING(10), allowNull: false }, stock: { type: Sequelize.INTEGER, allowNull: false }, original_price: { type: Sequelize.INTEGER, allowNull: false }, price: { type: Sequelize.INTEGER, allowNull: false }, // 新增 pay_type:{ type: Sequelize.STRING(10), allowNull: false }, summary: { type: Sequelize.STRING(1000), allowNull: false }, }); GoodParentCategory.hasMany(Good); Good.belongsTo(GoodParentCategory); export { Good }; 参考 Sequelize 迁移 Sequelize 更新数据库表字段 GitHub - sequelize/cli 本文同步分享在 博客“_龙衣”(CSDN)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

foy: 轻量级的基于 nodejs 的通用 build 工具

npm 的 scripts 下写的命令太多就很容易很乱,各种第三方轮子都只能解决一部分问题,总感觉不是很好用,想找个类似 make 的工具只能找到 jake, 可是 jake 的 API 太老,居然很多都不支持 promise, 代码也不多,就干脆自己造轮子了, 感觉效果还行。 项目地址: https://github.com/zaaack/foy gitee 地址: https://gitee.com/zane_young/foy 特点: 基于 promise 的任务和内置工具函数(fs/shell), 无缝支持 async/await 类似于 shelljs 的跨平台 shell dsl, 人人都会写 shell 易学易用,无需为写仅仅几个 build 命令而花费几个小时去寻找和学习第三方包 很小的安装成本 foy: gulp: grunt: 无缝和第三方支持 promise 的工具包整合,不需要封装成插件就能用 使用: 安装 yarn add -D foy # or npm i -D foy # Or Install globally with yarn add -g foy # or npm i -g foy 在项目根目录下增加一个 Foyfile.js (或者 Foyfile.ts, 需要安装 ts-node) import { task, desc, option, strict, fs } from 'foy' task('build', async ctx => { await ctx.exec('tsc') }) desc('Build ts files with tsc') option('-w, --watch', 'watch file changes') strict() // This will throw an error if you passed some options that doesn't defined via `option()` task('build2', async ctx => { await ctx.exec(`tsc ${ctx.options.watch ? '-w' : ''}`) }) task('task', async ctx => { await fs.rmrf('/some/dir/or/file') // Remove directory or file await fs.copy('/src', '/dist') // Copy folder or file let json = await fs.readJson('./xx.json') await ctx.env('NODE_ENV', 'production') await ctx.cd('./src') await ctx.exec('some command') // Execute an command let { stdout } = await ctx.exec('ls', { stdio: 'pipe' }) // Get the stdout, default is empty because it's redirected to current process via `stdio: 'inherit'`. }) 然后就可以运行任务了 # 安装在本地 node_modules 目录下 npx foy build npx foy build1 npx foy task # 安装在全局 foy build foy build1

优秀的个人博客,低调大师

NodeJS之 Express框架 app.use(express.static)

一 、设置静态文件目录 语法如下: app.use(express.static(_dirname + '/public')); //设置静态文件目录 注: 将静态文件目录设置为项目根目录 + ‘/public’,可以这样写 app.use(express.static(path.join(_dirname, 'public'))); 运用path模版的join方法效果和前面是等价的。这里需要引用path模块。 path.join():将多个参数组合成一个 path 二、静态资源文件和动态资源文件是什么? 静态资源文件:对于不同的用户,内容都不会变化的文件。比如不管张三还是李四访问百度,他们所看到的图片、css、js文件都是一样的,我们成为静态资源文件; 动态资源文件: 对于不同用户,做出不同反应的就是动态文件,例如张三李四登陆百度,百度会分别对他们显示“张三,你好”、“李四,你好”,那么负责这些动态逻辑的文件就是动态文件了。 三、 app.use() 方法的作用 app.use : 用来给path注册中间函数的。这个path默认是“/”,也就是处理任何请求,同时注意的是他会处理path下的子路径,如果设置path为‘/hello’,那么请求路径为‘/hello/’,'/hello/nihao','/hello/bye'这样的请求都会交给中间函数处理的。 于是我们现在知道了app.use(express.static(_dirname + '/public'))是将所有请求,先交给express.static(_dirname + '/public')来处理一下。 四、express.static() 方法的作用 为了提供对静态资源文件(图片,css,js文件)的服务,请使用Express内置的中间函数express.static. 传递一个包含静态资源的目录给express.static中间件用于立即开始提供文件。 比如用以下代码来提供public目录下的图片、css文件和js文件: app.use(express.static('public')); 现在可以加载public目录下的文件了: 比如: http://localhost:3000/image/jkk.png 通过多次使用 express。static中间件来添加多个静态资源目录: app.use(express.static('public')); app.use(express.static('file')); Express将会按照你设置静态资源目录的顺序来查看静态资源文件。 为了给静态资源文件创建一个虚拟的文件前缀(文件系统中不存在),可以使用express.static函数指定一个虚拟的静态目录,如下: app.use('/static', express.static('public')) 现在你可以使用‘/static’作为前缀来加载public文件夹下的文件了 比如: http:// localhost:3000/static/image/kitten.jpg 参考文档:https://blog.csdn.net/u010977147/article/details/60956502 http://www.cnblogs.com/slovey/p/9213631.html

优秀的个人博客,低调大师

nodejs爬虫获取漫威超级英雄电影海报

昨天去看了《复联3》的首映,当我提前15分钟进入影院的时候, 看到了粉丝们取票的长队, 顿时有一种跨年夜的感觉... 最近看了node爬虫的一些知识, 这里用node爬取一下漫威官网的电影海报! marvel // https://marvel.com/movies/all const request = require('superagent') const cheerio = require('cheerio') const fs = require('fs-extra') const path = require('path') let url = 'https://marvel.com/movies/all' // 获取图片url和图片名字 async function getUrlAndName(){ // 用于存储返回值 let imgAddrArray = [] // 请求资源 const res = await request.get(url) // 将获取的html, 转换为资源符$, 相当于python中的xpath语法的etree过程 const $ = cheerio.load(res.text) // 定位资源位置, 将图片资源,和图片名字, 以数组方式, 返回给调用函数 $('.row-item-image a').each(function(i, elem){ let movieName = $(this).attr('href').split('/').pop() let imgAddr = $(this).find('img').attr('src') imgAddrArray.push([imgAddr, movieName]) }) return imgAddrArray } // 下载图片 async function download(imgAndName){ // 拼接出, 当前资源的文件名 let filename = imgAndName[1] + '.jpg' console.log("爬取海报:", filename); // 获取图片二进制数据 const req = request.get(imgAndName[0]); // 保存图片 await req.pipe(fs.createWriteStream(path.join(__dirname, 'images', filename))); } // 创建文件夹, 控制整体流程 async function init(){ let imgAddrArray = await getUrlAndName() // 创建文件夹 try{ await fs.mkdir(path.join(__dirname, 'images')); } catch(err){ console.log("==>", err); } // 获取资源 for (let imgAddr of imgAddrArray){ await download(imgAddr); } } init() 运行结果 小结: 直观感受, node爬虫并没有python好用, 而且由于浏览器的同源限制, 在浏览器端跑node爬虫也会有些麻烦;node爬虫的优势:理论上讲,node默认的异步玩法, 能达到python的多线程爬虫的效果. 写爬虫, 还是老老实实用python吧!

优秀的个人博客,低调大师

redis,nodejs,php,pub/sub 实战: 微信语音识别

2015年5月22日 20:20:20 星期五 效果: 这边对微信说话, 浏览器端及时显示语音识别的文字 注意: 在连接socket.io时, 按下浏览器f12, 如果一直有请求不断的刷, 说明socket.io没有连接成功 代码: node.js server端 1 var module_path = '/usr/local/web/node/bin/node_modules/'; 2 var html = '<html> <head> <meta charset="utf-8"> <title>微信接口</title> <script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.js"></script> <script src="http://cdn.bootcss.com/socket.io/1.3.5/socket.io.js"></script> </head> <body> <div id="voice"> 语音识别:<br><br> </div> <script type="text/javascript"> var voice = document.getElementById("voice"); var socket = io.connect("http://www.zhangzhibin.com:1337"); socket.on("hello", function(data){console.log(data); socket.emit("hello", {"status":"ok"}); }); socket.on("emit", function(data){var t = data+"<br>"; voice.innerHTML += t; socket.emit("emit", {"get":"data"}); }); </script> </body> </html>'; 3 //链接redis 4 var redis = require(module_path+'redis'); 5 var redis_client = redis.createClient(6379, '127.0.0.1'); 6 redis_client.auth('123456'); 7 redis_client.on('error', function(error){ 8 console.log('redis-error: ' + error); 9 }); 10 11 //创建服务器 12 var http = require('http'); 13 var url = require('url'); 14 var fs = require('fs'); 15 var ch = false; 16 var server = http.createServer(function (req, res) { 17 //获取请求参数 18 var objReqArg = url.parse(req.url, true).query; 19 ch = objReqArg.openid; 20 if (ch) { 21 redis_client.subscribe(ch, function(e){ 22 console.log('channel: '+ ch); 23 }); 24 }; 25 //返回 26 res.writeHead(200, {'Content-Type': 'text/html'}); 27 res.end(html); 28 29 }); 30 server.listen(1337, 'www.zhangzhibin.com'); 31 32 //创建服务器 33 var io = require(module_path + 'socket.io').listen(server); 34 io.sockets.on('connection', function(socket){ 35 socket.on('hello', function(data){ 36 console.log(data); 37 }); 38 socket.emit('hello', function(data){ 39 console.log(data); 40 }); 41 redis_client.on('message', function(error, msg){ 42 socket.emit('emit', msg); 43 }); 44 }); 45 46 console.log('Server running at http://www.zhangzhibin.com:1337'); 浏览器端html代码: 就是上边代码的第二行变量 var html='....' 1 <html> 2 <head> 3 <meta charset="utf-8"> 4 <title>微信接口</title> 5 <script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.js"></script> 6 <script src="http://cdn.bootcss.com/socket.io/1.3.5/socket.io.js"></script> 7 </head> 8 <body> 9 <div id="voice"> 语音识别:<br><br> </div> 10 <script type="text/javascript"> 11 var voice = document.getElementById("voice"); 12 var socket = io.connect("http://www.zhangzhibin.com:1337"); 13 socket.on("hello", function(data){ console.log(data); socket.emit("hello", {"status":"ok"}); }); 14 socket.on("emit", function(data){ var t = data+"<br>"; voice.innerHTML += t; socket.emit("emit", {"get":"data"}); }); 15 </script> 16 </body> 17 </html> 微信端: 注意一点, 在语音回调函数中, 将语音识别的结果publish到某一个频道上即可 这时, 会触发上边第41行代码, 将文字发完浏览器端 下边是微信回调的代码: 1 public function voice() 2 { 3 // 每次发送消息都会post 来一份签名相关的数据 4 // $echostr = $this->checkSignature(); 5 // exit($echostr); 6 7 preg_match('#<FromUserName><!\[CDATA\[([a-zA-Z0-9_]+)\]#', $GLOBALS['HTTP_RAW_POST_DATA'], $matches1); 8 preg_match('#<Recognition><!\[CDATA\[([^\]]*)\]#', $GLOBALS['HTTP_RAW_POST_DATA'], $matches2); 9 $openid = !empty($matches1[1]) ? $matches1[1] : '0'; 10 $text = !empty($matches2[1]) ? $matches2[1] : '没听清...'; 11 12 $objRedis = iredis::getInstance(); 13 $objRedis->publish($openid, $text); 14 15 } 测试方法: 1. 关注我的微信公众号"xxx" 2. 发送消息"主播_username" 3. pc端打开http://www.zhangzhibin.com/wechat/index/zhubolist 4. 点击你刚才输入的用户名, 进入你的对话页面 5. 对微信发送语音消息, 即可在刚才的浏览器页面看到语音识别结果 冷知识点: php流处理 $_POST无法解释二进制流,需要用到$GLOBALS['HTTP_RAW_POST_DATA']或php://input $content=$GLOBALS['HTTP_RAW_POST_DATA'];//需要php.ini设置 $content=file_get_contents('php://input');//不需要php.ini设置,内存压力小

优秀的个人博客,低调大师

nodejs】让nodejs像后端mvc框架(asp.net mvc)一样处理请求--请求处理结果适配篇(7/8)

文章目录 前情概要 前面一大坨一大坨的代码把route、controller、action、attribute都搞完事儿了,最后剩下一部分功能就是串起来的调用。 那接下就说个说第二个中间件,也是最后一个中间件RequestHandler RequestHandler 中间件的注册 app.use一下就完事啦。在RouteHandler把路由处理好之后,接着就是RequestHandler真正的来调用我们的处理函数啦,也就是我们的action。 import { RequestHandler, RouteHandler } from 'gd-express-basic' //第二个中间件,拦截所有请求对路由做自动映射 RouteHandler(_app, controllers); //第三个中间件,处理请求 _app.use(RequestHandler); RequestHandler 请求处理中间件代码 从当前请求拿到对应的action描述对象,如果没有就继续往后面的中间件走,比如走到404。 new一个新的controller对象,并把req,res对象传入。 完成参数的自动解析 调用action,得到返回结果 判断返回结果是否view类型,如果是view类型则调用render来渲染页面,如果不是则返回该对象 判断需要返回的对象是否是jsoncallback调用方式,是的话就适配一下 7.完事儿 /** * 请求处理中间件 * * @export * @param {core.Request} req * @param {core.Response} res * @param {(core.NextFunction | undefined)} next */ export function RequestHandler(req: core.Request, res: core.Response, next: core.NextFunction | undefined) { //1. 从当前请求拿到对应的action描述对象,如果没有就继续往后面的中间件走,比如走到404。 var desc: ActionDescriptor = res.locals.actionDescriptor if (!desc) { return next && next(); } var cname = desc.ControllerName; new Promise((reslove, reject) => { var cType = desc.ControllerType;//*controller class对象 //2. new一个新的controller对象,并把req,res对象传入。 var c = new cType(req, res);//new 一个controller 对象出来 //3. 完成参数的自动解析 var agrs = bindActionParameter(desc.ControllerType, desc.ControllerTypeName, desc.ActionType, desc.ActionName, req) //4. 调用action,得到返回结果 var actionResult = desc.ActionType.apply(c, agrs) return reslove(actionResult) }).then(actionResult => { if (actionResult instanceof ViewResult) { //5. 判断返回结果是否view类型,如果是view类型则调用render来渲染页面,如果不是则返回该对象 Promise.resolve(actionResult.data).then(ViewActionResultData => { var findViewNamePath = actionResult.name[0] === '/' ? actionResult.name.substr(1) : (cname + '/' + actionResult.name) res.render(findViewNamePath, ViewActionResultData, (err, html) => { if (err) { next && next(err); } else { res.send(html); res.end(); } }); }).catch(function (viewDataError) { next && next(viewDataError); }); } else if (typeof actionResult !== 'undefined') { //process object send response json //6. 判断需要返回的对象是否是jsoncallback调用方式,是的话就适配一下 let resultData = req.query['callback'] ? req.query['callback'] + '(' + JSON.stringify(actionResult) + ')' : actionResult; res.send(resultData); res.end() } else { //process not response or origin response.render or response.send. process.nextTick((_res: any) => { if (!_res.finished) { _res.end(); } }, res) } }).catch(processRequestError => { next && next(processRequestError); }) } 经过RouteHandler、RequestHandler两个方法的串联调用,就把我们整个零散的功能就完整统一的进行了一次调用。从controller的发现、注册,action的发现、注册,action参数配置,route解析、匹配,action调用,处理结果适配输出。 在编码调试过程中,发现目前dotnet core mvc的中间件的某些思想和实现方式和express的中间件基本一致。果然,思想都是相同的,哈哈哈。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。