nodejs koa

2023-11-16

第一步:如何写一个基于node的hello world

①创建新的文件夹nodejsdemo

②在cmd命令行中进入新建的文件夹nodejsdemo 运行 npm init -y (生成package.json文件)

③在cmd命令行中进入新建的文件夹nodejsdemo 运行 npm i (生成package-lock.json文件)

此时就搭建好了能够运行一个基础node的环境了

第二步:

①创建一个js文件 (server.js)有了server.js文件 我们就可以开始写基于nodejs的代码了

②写一个helloworld的demo

分析一下上面的代码是什么意思呢 ?

var http = require('http');

//引入nodejs自带的http模块(那这个http模块是干嘛用的呢?你可以把它理解成对象 这个对象里有很多别人写好的方法 你只需要调用 人家就帮你做了 你应该做的事情 )

//http模块功能 可以把他理解成一个创建小型的web服务器,主要的作用就是 让你在浏览器中输入

//http://127.0.0.1:8888/能看到东西

所以你会看到下面的代码

http.createServer(function (request, response) {


    response.writeHead(200, {'Content-Type': 'text/plain'});


    response.end('Hello World\n');
}).listen(8888);

引入http模块调用 createServer方法 就是启动了一个web服务 后面的listen 只是给这个web服务指定一个端口

request/response

①request 请求 (广义上来说 当后端需要获取前端传来的参数时 浏览器会把参数放在哪个地方呢 就是封装在request里面)比如nodejs里面 就会有对应的request对象

②response 响应 (广义上来说 当后端需要向前端返回数据时,浏览器会把返回的参数放在哪个地方呢 就是封装在response对象里)

此时再看如下的代码

response.writeHead(200, {'Content-Type': 'text/plain'});//指定响应头


response.end('Hello World\n');//输出hello world

让我们运行起来看一看效果:

方式一:node .\server.js

方式二:

让我们看看效果

此时一个基于nodejs程序的demo就写好了

第三步:

此时会发现 这个demo里仅仅输出了一个helloworld 一点实际的作用都没有

所以我们要对它进行改造 改的更向一个实际的项目的样子

那么实际的项目应该长什么样子呢 ,实际的项目会有很多的url 而上面的例子很显然还不足以满足我们需求

http://127.0.0.1:8888/
http://127.0.0.1:8888/demo
http://127.0.0.1:8888/data

那我们能不能在上面的demo稍微改造 让他满足我们的需求呢

仔细分析后我们会发现 唯一不同的是什么 就是url: / /demo /data

那么我们怎么改造呢 举例 我们希望访问路径是/ 时返回helloworld 访问路径时/data 时返回hellodata

var http = require('http');


http.createServer(function (request, response) {



    if(request.url=='/'){


            
        response.writeHead(200, {'Content-Type': 'text/plain'});


        response.end('Hello World\n');


    }


    if(request.url=='/data'){
    
            
        response.writeHead(200, {'Content-Type': 'text/plain'});


        response.end('Hello Data\n');


    }





}).listen(8888);



console.log('Server running at http://127.0.0.1:8888/');

看代码 中 我们通过request获取到url然后稍加判断就能实现根据不同的url返回不同东西的效果

但是聪明的你们会发现 上面的代码太过繁琐 如果一个项目需要这么写 那后端就得累死了

那么有没有什么好的方式呢 ,于是基于以上方式封装好的框架就出来 比如express 和 即将使用的 koa2

全文以下皆以koa2 为示例:(科普:如果你们使用vite构建一个vue项目时 当你们npm run server启动服务时 就是打开了一个koa写好的web服务)

第四步:

那koa这个玩意怎么用呢?

首先我们得安装koa 在你的pakeage.json中添加 以下内容 然后npm i

"koa": "^2.14.1",
  "koa-bodyparser": "^4.4.0",
  "koa-router": "^12.0.0",
  "koa-session": "^6.4.0",
  "koa-views": "^8.0.0",
  "koa2-request": "^1.0.4"

此时我们就安装好了koa框架 那么怎么用呢 ?我们不如在新建一个文件 koaserver.js

接着我们写一份简单的demo (基于koa)

const Koa = require('koa');
const app = new Koa();  //引用koa
const Router = require('koa-router'); 
const router = new Router(); //引用路由
const views = require('koa-views')
const path = require('path')
const session = require('koa-session');
const bodyParser=require('koa-bodyparser'); 
const request = require("request");
const https = require('https');
const querystring = require('querystring');
const koa2Req = require('koa2-request')

//以上引入koa相关的模块  就像我们曾经引入http模块一样


//这里定义了一个访问路径/   也就是说当访问路径为/时  会自动执行下面的函数
router.get('/', async (ctx) => {


    ctx.body='hello /';  //返回体  也就是返回的内容
    ctx.status=200				 //返回的状态码  比如404  500  200  401
    
})


//把我们引用的koa  和  koa-route 绑定到一起 这样koa才知道有你写的这个路由
app.use(router.routes()); 



app.listen(8888);  //监听一个端口 就是http模块的listen 一模一样

我们稍加修改:

const Koa = require('koa');
const app = new Koa();
const Router = require('koa-router'); 
const router = new Router();
const views = require('koa-views')
const path = require('path')
const session = require('koa-session');
const bodyParser=require('koa-bodyparser'); 
const request = require("request");
const https = require('https');
const querystring = require('querystring');
const koa2Req = require('koa2-request')


router.get('/', async (ctx) => {


    ctx.body='hello /';
    ctx.status=200
    
})



router.get('/data', async (ctx) => {


    ctx.body='hello data';
    ctx.status=200
    
})



app.use(router.routes());


app.listen(8888);

我们看看效果

此时我们就会发现 我们再也不用向http模块那样 写一大推if去判断了 也能实现我们需要的功能 ,这就是框架的魅力。

如上 一个简单的基于koa的demo就此完成了。

第五步:

上面的代码都是返回一个字符串 ,似乎显得太过单调 离项目的样子似乎还差了点有趣的东西

那什么是有趣的呢 ,我们希望返回给前端的东西是具有可维护性的 可修改的,所以这个世界就出现了一个叫数据库的东西 下文我们以postgresql为例 别问我为什么 因为你们会用上它!!!

那怎么连接数据库呢 (篇幅有限 怎么安装数据库 自行blibli解决)

毋庸置疑 写nodejs自身肯定是没有实现数据库功能的,因为nodejs的开发者谁知道你会用哪种数据库呢 ,

(此时抛出一个疑问 如果让你自己去写一个基于nodejs 连接数据库的东西 你该怎么写?)

你应该写一个模块,就像http模块那样 让人家require一下 就可以用是最好的, 在这个美好的时代,大佬帮你写了不用写了

我们想要连接postgresql数据库 请在你的pakeage.json 文件里引入:然后npm i

"pg": "^8.11.0",
    "pg-hstore": "^2.3.4",
    "request": "^2.88.2",
    "sequelize": "^6.31.1"

此时啊 我们就正式踏上了nodejs操作数据库的征途

①nodejs 是 nodejs ,数据库是数据库 ,两者结合在一起 是不是需要一个渠道,在代码领域 我们把这个渠道叫做数据库连接

那么连接 长什么样子呢 ,该怎么写呢:

看上面代码 :我们先引用一个叫做pg的模块然后配置连接

(***注意 任何一种关系型数据的连接都分为 host:服务器ip地址 port:端口号 user:用户名 password:密码 database :数据库的名称)

上面的代码 就是创建了一个连接的桥梁 (渠道)

我们有了这个渠道那怎么用呢 ?不妨我们在写一个url 用来查询数据库的一些东西

router.get('/sqldemo', async (ctx) => {



    client.query('select *  from demo',(err,res)=>{
               
        console.log(res.rows);
    })


    ctx.body='';
    ctx.status=200
    
})

让我们看看效果如何:

你会看到 console.log打印出了数据库里的内容,但是页面是空白的 ,此时正常人的思想 把打印的东西赋值给ctx.body不就OK了吗 ?不妨我们试试看

router.get('/sqldemo', async (ctx) => {



    client.query('select *  from demo',(err,res)=>{
               
        // console.log(res.rows);
        ctx.body=res.rows;
    })


    // ctx.body='';
    ctx.status=200
    
})

看效果:

koa非常善意的给了我们一个ok

玄学登场了吗?我们并没有写ok,ok是鬼写的 。

这个问题的诞生 是因为异步,严格来说 js里的玄学 80% 都可能是因为异步,那怎么处理呢 ?

我们对它进行分装一下 :(我们把执行sql的代码啊 放在一个promise里面)

let select=async (accesstoken,slot)=>{


    return new Promise((resolve,reject)=>{


            client.query('select *  from demo',(err,res)=>{
               
                resolve(res.rows);
            })
        
    })


  


}




router.get('/sqldemo', async (ctx) => {



    var data=await  select()
    ctx.body={list:data}
    ctx.status=200


   
    
})

这样我们在看看返回的效果:

非常好,总算是看到了我们想要的东西。

到此一个大学生式的demo我们也算是写完了!!!

第六步:

******我们可以做它,但是不够优雅******

想象一下上面的东西有哪写问题啊?

①我们所有的访问路径都写在了一个叫koaserver.js的文件里,这就很扯淡,遐想一下,一个项目100个人搞,这个文件将成为什么鸟样

②我们执行查询数据库的语句时 因为异步的问题,我们啊单独写了一个函数专门查询,遐想一下,一个项目100000个sql语句 是不是得特么写200000个函数 ,这是不能容忍的 ,这是不可维护的

为了让项目维护性提高,为了出了bug的时候 我们能更快的甩锅 ,务必是要解决上面两个问题的

很显然 项目是不能向上面的demo那样开发的

为了更好的保留现场 我们在新建一个app.js文件

那怎么处理①中的问题呢?我们要对上面的写的代码进行封装

比如:

const {Client} = require('pg')
const client = new Client({user: 'postgres',host: '192.168.222.18',database: 'postgres',password: 'senqi1010',port: 5432,
})
client.connect();

这个连接啊应该是所有需要操作数据库的js文件都需要用的,我们新建一个文件夹叫做dbconfig 里面新建一个dbconnect.js存放我们的连接 并导出

我们在新的app.js里面

const Koa = require('koa');
const app = new Koa();
const Router = require('koa-router'); 
const router = new Router();
const views = require('koa-views')
const path = require('path')
const session = require('koa-session');
const bodyParser=require('koa-bodyparser'); 
const request = require("request");
const https = require('https');
const querystring = require('querystring');
const koa2Req = require('koa2-request')

/* 上面引入所需模块 */


//引入自己写好的js文件
const liutianming = require('./routes/liutianming/route.js')



//让route模块加载我们写好的js文件
router.use('/liutianming',liutianming)

//让koa和route绑定到一起
app.use(router.routes());


app.listen(8888);

让我们看看我们如何写一个自己的js文件吧 (项目根目录新建routes目录 里面新建自己名字的文件夹 新增一个route.js文件 ** 文件夹和文件的名称随便 要和const liutianming = require('./routes/liutianming/route.js')

路径对的上就行),在看看自己的js里面写啥呢

const router = require('koa-router')()
const request = require("request");
const https = require('https');



const koa2Req = require('koa2-request')

/* 引用模块 */
const client =require("../../dbconfig/dbconnect.js");//获取到连接


//promise函数  返回查询数据库结果
let select=async (accesstoken,slot)=>{


    return new Promise((resolve,reject)=>{


            client.query('select *  from demo',(err,res)=>{
               
                resolve(res.rows);
            })
        
    })


  


}

/* 查询的url  那么怎么访问到这个呢    
  还记得我们在app.js里面配置的 router.use('/liutianming',liutianming)
	那么访问路径就变成了 http://127.0.0.1:8888/liutianming/select
*/
router.get('/select', async (ctx) => {


    var data=await  select()
    ctx.body={list:data}
    ctx.status=200


})

//会看到 add 往数据库新增数据时  删除数据时  修改数据时没有写promise 
//因为这里没考虑返回给前端内容如果有还是的异步
router.get('/add', async (ctx) => {


    var addSql = 'INSERT INTO demo(name) VALUES($1)';
   
    var addSqlParams = ['nodejs'];
    client.query(addSql,addSqlParams,function (err, result) {
  
    });


    ctx.body={list:'ok'}
    ctx.status=200


})


router.get('/del', async (ctx) => {


    var addSql = 'DELETE FROM demo WHERE id=1'


    client.query(addSql,function (err, result) {})


    ctx.body={list:'ok'}
    ctx.status=200


})


router.get('/update', async (ctx) => {


    var addSql = 'UPDATE demo SET name=\'AA\' where id=2'


    client.query(addSql,function (err, result) {})


    ctx.body={list:'ok'}


    ctx.status=200


})



module.exports = router.routes()

此时我们已经解决的了所有的url都写在一个文件里的尴尬 ,还有一个尴尬 ,还是那句话 一个项目一万句查询语句要写二万个函数 ,好累,太特么扯淡了 ,怎么办

不需要 紧张,到了这里 你们都应该知道了,没啥 肯定曾有个大佬又帮我们封装好了一个模块,没错就是这样,大佬正在用一个巨大的笼子把你们限定在增删改查的笼子里,你不需要知道他们做了啥,只需要知道你要去干啥

这个模块叫做 sequelize 一种基于nodejs orm连接数据库的方式 ,问题来了 什么orm 完全就不知所云

不妨我们百度一下 :

简单来说 ,就是创建一个类 类里的变量名和类型与数据库中表的字段一一对应上

比如说 :

数据库里有个表叫做demo ,那么我们可以创建一个类名字叫做demo ,表里有一个字段叫做name 那么我们demo类里有一个属性叫做name,晕了没有 看图

具体怎么用呢?

①之前我们创建连接的方式不行了 既然要用sequelize库就得按sequelize的规矩

1.创建一个数据库的配置文件(根目录下创建文件夹dbconfigorm 里面创建dbconfig.js)

module.exports = {
    HOST: "192.168.222.18",
    USER: "postgres",
    PASSWORD: "senqi1010",
    DB: "postgres",
    dialect: "postgres",
    pool: {
      max: 5,
      min: 0,
      acquire: 30000,
      idle: 10000
    }
  };

2.创建连接数据库的渠道 也就是连接 桥梁(根目录下创建文件夹dbconnectorm 里面创建dbconnect.js)

const dbConfig =require("../dbconfigorm/dbconfig.js");//获取到配置



const Sequelize = require("sequelize");
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
  host: dbConfig.HOST,
  dialect: dbConfig.dialect,


  pool: {
    max: dbConfig.pool.max,
    min: dbConfig.pool.min,
    acquire: dbConfig.pool.acquire,
    idle: dbConfig.pool.idle
  }
});



module.exports = sequelize;

②接着我们就可以使用它了(为了做到环境隔离 我们在routes下新建一个dataormop.js文件用来使用sequelize操作数据库)

const router = require('koa-router')()
const request = require("request");
const https = require('https');



const koa2Req = require('koa2-request')


const sequelize=require("../dbconnectorm/dbconnect.js");

router.get('/orisql', async (ctx) => {


  
    const demo = await sequelize.query("SELECT * FROM demo");
    ctx.body={list:demo}


    ctx.status=200


})



router.get('/orisql2', async (ctx) => {


    var id=3
    const res = await sequelize.query(`SELECT * FROM demo where id=${id}` ,{
        model: demo,
        mapToModel: true 
    });
    ctx.body={list:res}


    ctx.status=200


})



router.get('/orisql3', async (ctx) => {


   
    const res = await sequelize.query(`SELECT * FROM demo where id=?` ,{
        
        replacements: [3],
        model: demo,
        mapToModel: true 
    });
    ctx.body={list:res}


    ctx.status=200


})




router.get('/orisql4', async (ctx) => {


  
    const res = await sequelize.query('SELECT count(*) as countnum FROM demo');
    ctx.body={list:res}


    ctx.status=200


})


module.exports = router.routes()

到此为止:对于数据库的操作也算是接近尾声。(我的demo里会提供基于orm的方式操作数据,但是需要对数据库非常清晰,请自行要就学习)

第七步:

现在我们可以做到前端访问后端,后端查询后数据库把数据返回给前端 ,这样看已经很像一个项目了,但似乎又少了一点什么

少了啥了 ,就是访问权限(记得和后面的认证权限区分开)

什么叫访问权限 就是 带token访问

什么叫认证权限 就是 这个人有没有资格访问我这个url

(两者的区别 访问权限 你有没有资格进去 认证权限 你进去了有没有资格动屋里 的东西)

这里介绍一下 常用的jwt 访问权限:

举一个流程例子:

***当用户只有登录成功的时候才能进去首页 那么 系统里除了登录/注册这些功能外 其余的功能是不是都需要一个权限 怎么实现了 就是当前端提交用户名和密码到后端后,后端判断是否正确如果正确那么返回给前端一个token(也就是很长的字符串),前端拿到后放入到以后请求的header里面,每当一个请求来时都要先判断一个这个token有没有效(下面我们仅仅用代码去实现jwt 需要packege.json中添加jsonwebtoken)

①我们要回到app.js 这是我们的主文件

const Koa = require('koa');
const app = new Koa();
const Router = require('koa-router'); 
const router = new Router();
const views = require('koa-views')
const path = require('path')
const session = require('koa-session');
const bodyParser=require('koa-bodyparser'); 
const request = require("request");
const https = require('https');
const querystring = require('querystring');
const koa2Req = require('koa2-request')


const liutianming = require('./routes/liutianming/route.js')


const orms = require('./routes/dataormop.js')

router.use('/liutianming',liutianming)

router.use('/orm',orms)

app.use(router.routes());

app.listen(8888);

看上面代码 我们新增了两个路由一个liutianming 指向一个一个js(用client操作数据库) 一个 orm(用sequelize操作数据库)

那我们现在就将给这个url添加权限 怎么弄呢

第一种:

我们想下 如果我们要给所有的url添加权限 最简单的判断 每个url对应的函数里去验证一下token(此方法坏处:要写好多代码 ,好烦好扯淡)

第二种:

koa提供了一个钩子函数:就是在执行所有的url前都要执行一个函数 这个就避免了我们写那么多(直接上代码)

const Koa = require('koa');
const app = new Koa();
const Router = require('koa-router'); 
const router = new Router();
const views = require('koa-views')
const path = require('path')
const session = require('koa-session');
const bodyParser=require('koa-bodyparser'); 
const request = require("request");
const https = require('https');
const querystring = require('querystring');
const koa2Req = require('koa2-request')
const jwt = require('jsonwebtoken');


const liutianming = require('./routes/liutianming/route.js')


const whiteList = ['/login','/reg']


const orms = require('./routes/dataormop.js')


function verifyToken(token) {
 
    return new Promise((resolve, reject) => {
      jwt.verify(token, 'key', {ignoreNotBefore: true}, (error, result) => {
        if(error){
          resolve({code:401,msg:'token expired'})
        } else {
          resolve(result)
        }
      })
    })
}


app.use(async (ctx,next) => {
 
    if(!whiteList.includes(ctx.request.url)) {
      
        //在线获取新的token(php版本)
        const resurl = await koa2Req({
          url: 'http://101.35.96.100/juyan/index/login?username=admini&password=a123456'
          });
          let body = JSON.parse(resurl.body);
          token=body.token
        
        //这是一个过期的token
        //token='eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6ImtleUlkIn0.eyJpc3MiOiJteSIsImF1ZCI6InB1YmxpYyIsImlhdCI6MTY4MzM1MjcwNiwibmJmIjoxNjgzMzUyNzA2LCJleHAiOjE2ODM5NTc1MDYsImRhdGEiOnsiaWQiOjEsInVzZXJuYW1lIjoiYWRtaW5pIiwicGFzc3dvcmQiOiJhMTIzNDU2IiwiZW1haWwiOiIxMjNAMTYzLmNvbSIsInJlYWxuYW1lIjoiMTIzIiwicGhvbmUiOiIxNTIzNzE1NjU3MyIsImltZyI6Imh0dHBzOi8vbmV3c2Fhcy53dWhhbnpodWFuZ3hpdTAxLmNuL2NyLTE4MmMxY2VmYjlmZDllNDk4MDY5YWM2NTEyMzMyZTI1MjAyMzAyMTEuanBnIiwicmVnX2lwIjoiMTI3LjAuMC4xIiwibG9naW5fdGltZSI6MTY4MDU3Mjk2NywibG9naW5faXAiOiIzOS4xNDkuMTIuMTg0IiwidXBkYXRlX3RpbWUiOjE2ODA1NzI5NjcsImlzX2VuYWJsZWQiOjEsImdyb3VwX2lkIjoxLCJjcmVhdGVfdGltZSI6MTU0MDk3NTIxMywiZGVsZXRlX3RpbWUiOjB9fQ.rB7rUIw6N5xOhgR036yytWE1wnw_z8PcYMEO4NErk0Y'
        
        //生成jwt token(nodejs版本)
        // const token =  jwt.sign({id:1}, 'key', { expiresIn:60*60})
        const res=await verifyToken(token)
        // console.log(res)
  
        if(res.code==401){
       
          ctx.status = 401;
          ctx.body = res;
  
  
        }else{
          await next()
        }
  
        
  
  
  
     
    } else {
      
      await  next()
    }
  
   
});


// router.get('/', async (ctx) => {

//     ctx.body='hello /';
//     ctx.status=200
    
// })


// router.get('/', async (ctx) => {

//     ctx.body='hello /';
//     ctx.status=200
    
// })




router.use('/liutianming',liutianming)

router.use('/orm',orms)

app.use(router.routes());

app.use(router.allowedMethods({ 
  
}));

app.listen(8888);

附注资料:

①如何处理跨域问题

方法一:引入koa2-cors包

const cors = require('koa2-cors');



/* 若添加配置项  可以手动设置允许哪些域名访问能跨域 比如 来自www.baidu.com的域名的请求允许跨域*/
app.use(
  cors({
    origin: function(ctx) { //设置允许来自指定域名请求
      return 'http://www.baidu.com'; //只允许http://www.baidu.com这个域名的请求
    },
    maxAge: 5, 
    credentials: true, //是否允许发送Cookie
    allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], //设置所允许的HTTP请求方法
    allowHeaders: ['Content-Type', 'Authorization', 'Accept'], //设置服务器支持的所有头信息字段
    exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] //设置获取其他自定义字段
  })
);

此时就可以跨域了

方法二:

还记得之前前面讲的验证token的时候,我们讲了一个

app.use(async (ctx,next) => {
 
    if(!whiteList.includes(ctx.request.url)) {
      
        //在线获取新的token(php版本)
        const resurl = await koa2Req({
          url: 'http://101.35.96.100/juyan/index/login?username=admini&password=a123456'
          });
          let body = JSON.parse(resurl.body);
          token=body.token
        
        //这是一个过期的token
        //token='eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6ImtleUlkIn0.eyJpc3MiOiJteSIsImF1ZCI6InB1YmxpYyIsImlhdCI6MTY4MzM1MjcwNiwibmJmIjoxNjgzMzUyNzA2LCJleHAiOjE2ODM5NTc1MDYsImRhdGEiOnsiaWQiOjEsInVzZXJuYW1lIjoiYWRtaW5pIiwicGFzc3dvcmQiOiJhMTIzNDU2IiwiZW1haWwiOiIxMjNAMTYzLmNvbSIsInJlYWxuYW1lIjoiMTIzIiwicGhvbmUiOiIxNTIzNzE1NjU3MyIsImltZyI6Imh0dHBzOi8vbmV3c2Fhcy53dWhhbnpodWFuZ3hpdTAxLmNuL2NyLTE4MmMxY2VmYjlmZDllNDk4MDY5YWM2NTEyMzMyZTI1MjAyMzAyMTEuanBnIiwicmVnX2lwIjoiMTI3LjAuMC4xIiwibG9naW5fdGltZSI6MTY4MDU3Mjk2NywibG9naW5faXAiOiIzOS4xNDkuMTIuMTg0IiwidXBkYXRlX3RpbWUiOjE2ODA1NzI5NjcsImlzX2VuYWJsZWQiOjEsImdyb3VwX2lkIjoxLCJjcmVhdGVfdGltZSI6MTU0MDk3NTIxMywiZGVsZXRlX3RpbWUiOjB9fQ.rB7rUIw6N5xOhgR036yytWE1wnw_z8PcYMEO4NErk0Y'
        
        //生成jwt token(nodejs版本)
        // const token =  jwt.sign({id:1}, 'key', { expiresIn:60*60})
        const res=await verifyToken(token)
        console.log(res)
  
        if(res.code==401){
       
          ctx.status = 401;
          ctx.body = res;
  
  
        }else{
          await next()
        }
  
        
  
  
  
     
    } else {
      
      await  next()
    }
  
   
});

其实处理跨域的问题的原理就是在服务端给浏览器响应的时候都在请求头上添加一些东西,这些东西的作用就是让浏览器任何这个会话的行为不存在不符合同源策略的情况,所以我们改造一下我们的代码:

app.use(async (ctx,next) => {


    ctx.set('Access-Control-Allow-Origin', '*'); //允许来自所有域名请求(不携带cookie请求可以用*,如果有携带cookie请求必须指定域名)
    // ctx.set("Access-Control-Allow-Origin", "http://localhost:8080"); // 只允许指定域名http://localhost:8080的请求


    ctx.set('Access-Control-Allow-Methods', 'OPTIONS, GET, PUT, POST, DELETE'); // 设置所允许的HTTP请求方法


    ctx.set('Access-Control-Allow-Headers', 'x-requested-with, accept, origin, content-type'); // 字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段.
    // 服务器收到请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。


    ctx.set('Content-Type', 'application/json;charset=utf-8'); // Content-Type表示具体请求中的媒体类型信息


    ctx.set('Access-Control-Allow-Credentials', true); // 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。
    // 当设置成允许请求携带cookie时,需要保证"Access-Control-Allow-Origin"是服务器有的域名,而不能是"*";


    ctx.set('Access-Control-Max-Age', 300); // 该字段可选,用来指定本次预检请求的有效期,单位为秒。
    // 当请求方法是PUT或DELETE等特殊方法或者Content-Type字段的类型是application/json时,服务器会提前发送一次请求进行验证
    // 下面的的设置只本次验证的有效时间,即在该时间段内服务端可以不用进行验证


    ctx.set('Access-Control-Expose-Headers', 'myData'); // 需要获取其他字段时,使用Access-Control-Expose-Headers,



 
    if(!whiteList.includes(ctx.request.url)) {
      
        //在线获取新的token(php版本)
        const resurl = await koa2Req({
          url: 'http://101.35.96.100/juyan/index/login?username=admini&password=a123456'
          });
          let body = JSON.parse(resurl.body);
          token=body.token
        
        //这是一个过期的token
        //token='eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6ImtleUlkIn0.eyJpc3MiOiJteSIsImF1ZCI6InB1YmxpYyIsImlhdCI6MTY4MzM1MjcwNiwibmJmIjoxNjgzMzUyNzA2LCJleHAiOjE2ODM5NTc1MDYsImRhdGEiOnsiaWQiOjEsInVzZXJuYW1lIjoiYWRtaW5pIiwicGFzc3dvcmQiOiJhMTIzNDU2IiwiZW1haWwiOiIxMjNAMTYzLmNvbSIsInJlYWxuYW1lIjoiMTIzIiwicGhvbmUiOiIxNTIzNzE1NjU3MyIsImltZyI6Imh0dHBzOi8vbmV3c2Fhcy53dWhhbnpodWFuZ3hpdTAxLmNuL2NyLTE4MmMxY2VmYjlmZDllNDk4MDY5YWM2NTEyMzMyZTI1MjAyMzAyMTEuanBnIiwicmVnX2lwIjoiMTI3LjAuMC4xIiwibG9naW5fdGltZSI6MTY4MDU3Mjk2NywibG9naW5faXAiOiIzOS4xNDkuMTIuMTg0IiwidXBkYXRlX3RpbWUiOjE2ODA1NzI5NjcsImlzX2VuYWJsZWQiOjEsImdyb3VwX2lkIjoxLCJjcmVhdGVfdGltZSI6MTU0MDk3NTIxMywiZGVsZXRlX3RpbWUiOjB9fQ.rB7rUIw6N5xOhgR036yytWE1wnw_z8PcYMEO4NErk0Y'
        
        //生成jwt token(nodejs版本)
        // const token =  jwt.sign({id:1}, 'key', { expiresIn:60*60})
        const res=await verifyToken(token)
        console.log(res)
  
        if(res.code==401){
       
          ctx.status = 401;
          ctx.body = res;
  
  
        }else{
          await next()
        }
  
        
  
  
  
     
    } else {
      
      await  next()
    }
  
   
});

②路由前缀:

比如我们写好了接口

http://127.0.0.1:8888/orm/select

http://127.0.0.1:8888/orm/add

http://127.0.0.1:8888/orm/del

我们需要给项目所有的接口都添加一个前缀比如

http://127.0.0.1:8888/node/orm/select

http://127.0.0.1:8888/node/orm/add

http://127.0.0.1:8888/node/orm/del

则需要配置

router.prefix('/node')

③文件上传

方法一:安装 npm i koa-multer

添加代码

const Multer=require("koa-multer")


let storage=Multer.diskStorage({
  destination:function(req,file,cb){
      cb(null,"upload")
  },
  filename:function(req,file,cb){
      var fileFormat = (file.originalname).split(".");
      cb(null,Date.now() + "." + fileFormat[fileFormat.length - 1]);
  }
})
let upload=Multer({storage:storage})
router.post("/upload",upload.single("file"),async ctx=>{
  console.log("ctx.req:",ctx.req)
  ctx.body={
     msg:"上传成功"
  }
})

方法二:

安装koa-body npm i koa-body

const { koaBody }= require('koa-body'); //解析上传文件的插件
app.use(koaBody({
        multipart: true,
        formidable: {
            uploadDir:path.join(__dirname,'upload/'),
            maxFileSize: 2000 * 1024 * 1024    // 设置上传文件大小最大限制,默认2M
        }
}))

router.post("/upload",async (ctx)=>{
  
 
  const file = ctx.request.files.file  //得到文件对象


  /* 创建一个读流 (输入流):怎么理解呢 ?在内存或者磁盘开辟一个临时的存储空间,
  把文件对象的东西读出来放到这个临时的存储空间*/
  const reader = fs.createReadStream(file.filepath);   

	//创建一个存放文件的路径  比如 upload文件夹下1.png文件
  //   upload/1.png
  let filePath = 'upload/' + `/${file.originalFilename}`;

  /* 创建一个写流(输出流):可以理解成一个指向文件的指针*/
  const upStream = fs.createWriteStream(filePath);
  

  /* 创建一个写流 :怎么理解呢 ?把刚才在内存或者磁盘开辟一个临时的存储空间
  的内容通过刚才新建的写流 写入到上面filepath中  这样就会在upload文件夹下生成1.png这张图片*/
  reader.pipe(upStream);


  //可以把文件对象返回前端 看看都是写什么东东
  ctx.body = JSON.stringify(ctx.request.files);
})

④提交get/post请求 之 https版

安装koa2-request/request 运行npm i koa2-request

const koa2Req = require('koa2-request')
const https = require('https');

//举例向微信提交get请求 获取到小程序accesstoken
router.get('/general_accesstoken', async (ctx) => {



  const res = await koa2Req({
      url: 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='+ctx.query.appid+'&secret='+ctx.query.secret
      });
  let body = JSON.parse(res.body);



  ctx.body=body.access_token
  ctx.status=200
 


  
})
//提交https post请求
const request = require("request");
const https = require('https');

let publisher_adpos_general=async (accesstoken)=>{


    return new Promise((resolve,reject)=>{


        request({
            timeout:5000,    // 设置超时
             method:'GET',    //请求方式
             url:'https://api.weixin.qq.com/publisher/stat?action=publisher_adpos_general&page=0&page_size=0&start_date=2023-05-21&end_date=2023-05-21', //url
             qs:{                                                  //参数,注意get和post的参数设置不一样
              access_token:accesstoken,
               
             }
    
           },function (error, response, body) {
              if (!error && response.statusCode == 200) {
                resolve(body)
                
               
    
              }else{
              
                reject(error)
                
              }
          });


        
    })


  


}
router.get('/publisher_adpos_general', async (ctx) => {


                var data=null;


                let res =await  publisher_adpos_general(ctx.query.accesstoken)


                ctx.body=res;
                ctx.status=200


               
    
   
})

⑤pm2管理器

由于js是单线程应用 一旦报错 整个进程就会再也无法继续进行下去,于是一种以守护进程的方法启动项目的方式就此出现 这个叫做pm2

1.安装pm2 npm install -g pm2

2.使用 pm2 start 文件名(比如:app.js server.js koaserver.js)

我们会看到 并不像 node ./app.js 那样卡在了控制台 可以理解 后台默默的启动了

3.我们启动多个nodejs的项目

4.如何关闭pm2

首先我们能在pm2里面看到我们启动了两个项目 app 和 server

关闭命令 pm2 delete app / pm2 delete server

番外版:

①七牛云文件上传

以前我们讲的都是前端上传文件到服务器上 ,在市场上还有一种叫做第三方对象存储 ,七牛云就是其中的一种

1.安装qiniu 运行npm i qiniu

2.现在我们对之前上传到服务器的代码进行改装

router.post("/upload",async (ctx)=>{
  
 
  const file = ctx.request.files.file


  // const reader = fs.createReadStream(file.filepath);


  // let filePath = 'upload' + `\\${file.originalFilename}`;


  // const upStream = fs.createWriteStream(filePath);


  // reader.pipe(upStream);


  const data=await uploadFile(file.filepath, file.originalFilename) ;


  ctx.body = JSON.stringify(data);


})


function uploadFile(reader, key) {
  return new Promise((resolve, reject) => {
      /* 七牛后台配置 */
      var ACCESS_KEY = '';
      var SECRET_KEY = '';
      var bucket = '';
      const domain = '';
    
      const mac = new qiniu.auth.digest.Mac(ACCESS_KEY, SECRET_KEY);
      const config = new qiniu.conf.Config();
      config.zone = qiniu.zone.Zone_z0;
      const formUploader = new qiniu.form_up.FormUploader(config);
      const putExtra = new qiniu.form_up.PutExtra();
      
      // 生成上传凭证
      const options = {
        scope: bucket + ':' + key,
      };
      const putPolicy = new qiniu.rs.PutPolicy(options);
      const uploadToken = putPolicy.uploadToken(mac);
      
      // 上传文件
      formUploader.putFile(uploadToken, key, reader, putExtra, function (err, body, info) {
       
        if (err) {
          reject(err);
        } else {

          resolve(domain + '/' + key);
        }
      });
      });
}

②nodejs 使用websocket 网络通信

1.安装nodejs-websocket 运行npm i nodejs-websocket

2.新建ws.js文件

nodejs代码:

const ws = require('nodejs-websocket');
const TYPE_ENTER = 0
const TYPE_LEAVE = 1
const TYPE_MSG = 2


//记录当前连接上的用户登录
let count = 0
const server = ws.createServer(conn =>{
    console.log('新的连接')
    count++
    conn.userName = `用户${count}`
    broadcast({
        type: TYPE_ENTER,
        msg: `${conn.userName}进入了聊天室`,
        time: new Date().toLocaleDateString()
    })


    conn.on('text',data=>{
        //2. 当接收到某个用户信息是,发给所有人
        broadcast({
            type: TYPE_MSG,
            msg: data,
            time: new Date().toLocaleTimeString()
        })
    })


    conn.on('close',data=>{
        //3.有人退出 告诉所有人
        console.log('关闭连接')


        count--
        broadcast({
            type: TYPE_LEAVE,
            msg:`${conn.userName}离开了聊天室`,
            time: new Date().toLocaleTimeString()
        })
    })


    conn.on('error',data=>{
        console.log('发生异常')
    })


})
// 广播 给所有人发消息
function broadcast(msg) {
    //server.connections : 表示所有的用户
  
    server.connections.forEach(item=>{
        item.send(JSON.stringify(msg))
    })
}



server.listen(2349,()=>{
    console.log('监听端口2349')
})

原生html代码:

<html>

<head>

<title>WebSocket测试</title>

<script>

var ws = null;

function send() {

var message = document.getElementById("text").value;

ws.send(message);

}

function connect() {

if(ws == null)

{

ws = new WebSocket("ws://127.0.0.1:2349/");

ws.onopen = function ()

{

alert("连接成功");

};

ws.onmessage = function (evt)

{

var received_msg = evt.data;

document.getElementById("showMes").value += evt.data + "\n";

};

ws.onclose = function ()

{

alert("断开了连接");

};

}

}

function closeSocket()

{

ws.close();

ws = null;

}

window.onload = function(){

}

</script>

</head>

<body>

<input type="button" οnclick="connect()" value="连接" />

<input type="button" οnclick="closeSocket()" value="关闭" />

<input type="button" οnclick="send()" value="发送" />

<input type="text" id="text" />

<br/>

<textarea rows="3" cols="30" id="showMes" style="width:100%;height:500px;"></textarea>

</body>

</html>

运行如下:

vue代码(新增一个测试组件 ChatDemo.vue)

<template>
    <div>
        <input type="button" @click="connect()" value="连接" />
        <input type="button" @click="closeSocket()" value="关闭" />
        <input type="button" @click="send()" value="发送" />    
        <input type="text" id="text" v-model="msg"/>
        <br/>
        <textarea rows="3" cols="30" ref="showMes" style="width:100%;height:500px;"></textarea>   
    </div>
</template>
<script>



export default {
  data() {
    return {
      wsUrl: 'ws://127.0.0.1:2349/',
      websock: null, //ws实例
      msg:''
    
    };
  },



  mounted() {
 


    //初始化websocket,此页面建立了2个长链接
    // this.initWebSocket();


  },
  destroyed() {
    //离开路由之后断开websocket连接
    this.websock.close();
 
  },
 
  methods: {
    connect(){


        this.initWebSocket();


    },
    closeSocket(){


        this.websocketclose()
    },
    send(){
        this.websock.send(this.msg);
    },
    //初始化Websocket--sys_info
    initWebSocket() {
      if (typeof WebSocket === "undefined")
        return console.log("您的浏览器不支持websocket");
      this.websock = new WebSocket(this.wsUrl);
      this.websock.onmessage = this.websocketonmessage;
      this.websock.onopen = this.websocketonopen;
      this.websock.onerror = this.websocketonerror;
      this.websock.onclose = this.websocketclose;
    },
    websocketonopen() {
      
    //   let action = { message: "sys_info" };
    //   this.websocketsend(JSON.stringify(action));
    },
    websocketonerror() {
      //链接建立失败重连
      
      this.initWebSocket();
    },
    websocketonmessage(e) {
      //数据接收
      const redata = JSON.parse(e.data);
      console.log(redata)
      this.$refs.showMes.value += JSON.stringify(redata) + "\n";


    
    },
    websocketsend(Data) {
      //数据发送
      // console.log("数据发送", Data);
      this.websock.send(Data);
    },
    websocketclose(e) {
      //关闭
      // console.log("断开链接", e);
      this.websock.close();
    },


   


  
  },
};




</script>
<style>
</style>

效果图:(先运行html然后运行vue)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

nodejs koa 的相关文章

  • 通过 HTTPS 加载页面但请求不安全的 XMLHttpRequest 端点

    我有一个页面 上面有一些 D3 javascript 该页面位于 HTTPS 网站内 但证书是自签名的 当我加载页面时 我的 D3 可视化效果不显示 并且出现错误 混合内容 页面位于 https integration jsite com
  • 为什么 lodash 将我的数组转换为对象?

    我是 lodash 的新手 创建了一个函数 该函数从值为 null 或空白的对象中删除键 但是当我传递包含某些部分作为数组的对象时 它会删除数组并将其转换为对象 下面是我尝试过的代码 mixin removeFalsies this rem
  • 在 JavaScript 中计算不包括周末和节假日的天数

    我正在尝试编写一个代码 其中将计算总天数 不包括周末和自定义假期 我通过 stackoverflow 和 adobe 论坛进行搜索以找到解决方案 并提供了以下代码 如果公共假期恰逢工作日 周六至周三 则不计算在内 我的问题是 如果公共假期落
  • 添加选中的单选按钮的总数

    UPDATE 如果您尝试此链接上的表格http jsfiddle net Matt KP BwmzQ http jsfiddle net Matt KP BwmzQ 按下小提琴并选择右上角的 40 英镑单选按钮 然后在底部看到订单总额 上面
  • 在设置后用 Javascript 替换 'var' css 属性

    我有一个元素 其上设置了 var 属性 如下所示 div class divwithbackground div CSS divwithbackground after background image var page header se
  • 在 d3 中应用转换时出现错误

    我正在尝试对我在 d3 中设计的条形图应用一些过渡效果 这是我的代码 svg selectAll bar data data enter append g attr class bar append rect attr rx barRadi
  • Sequelize 关联 - 请改用 Promise 风格

    我正在尝试将 3 张桌子连接在一起Products Suppliers and Categories然后排SupplierID 13 我读过了如何在sequelize中实现多对多关联 https stackoverflow com a 25
  • 按 Enter 键提交消息?

    我正在开发一个基于本教程使用 Meteor 构建的聊天应用程序 http code tutsplus com tutorials real time messaging for meteor with meteor streams net
  • 为什么人们将自己的自定义/用户函数添加到 jQuery 对象中? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我见过人们将自己的自定义 用户功能添加到jQuery目的 例如 myUserFunc function regular JS code 你为什么
  • Durandal SPA 与打字稿有关的问题

    我使用 TypeScript 1 8 将我的 durandal SPA 应用程序从 VS 2012 更新到 VS 2015 它将生成 JavaScript ECMA5 我解决了所有构建错误 但我无法修复一个名为 return 语句只能在函数
  • 当系列没有相同的时间值时,如何在工具提示中显示所有系列

    我有一个显示多个时间序列的图表 不同时间序列不会同时采样 有没有办法在工具提示中显示所有系列 在示例中 您可以看到所有系列都包含在前 2 个点的工具提示中 因为它们是同时采样的 其余点仅包含 1 个系列 var myChart echart
  • React Native 中 fontAwesome 图标的圆形轮廓

    我想使用 fontAwesome 图标 使其位于圆圈的中间 我想将它用作一个图标项 我读到我们可以将它与圆形图标一起使用并将其放置在其中 但我无法使其工作 import IconFA from react native vector ico
  • 2 个 SVG 路径的交集

    我需要检查两个 SVG Path 元素是否相交 检查边界框与 getBBox 太不准确了 我目前正在做的是迭代两条路径 getTotalLength 然后检查是否有两个点 getPointAtLength 是平等的 下面是一个片段 但正如您
  • 如何创建显示/隐藏 Docusaurus 项目中所有详细标签状态的按钮?

    根据讨论here https stackoverflow com questions 58579048 how to add or remove the open attribute from all details tags in a r
  • Lodash _.hasIntersection?

    我想知道两个或多个数组是否有共同的项目 但我不在乎这些项目是什么 我知道 lodash 有一个 intersection方法 但我不需要它来遍历每个数组的每个项目 相反 我需要类似的东西 hasIntersection一旦找到第一个常见的出
  • 数据表 - 从 AJAX 源过滤数据

    我有一个数据表 正在从 api 获取数据 现在我的状态是活动的 非活动的 如果标志是活动的 那么我需要在数据表中显示 否则我不应该显示过期的 这是我的fiddle https jsfiddle net lakshmipriya001 qLp
  • 使用 NodeJS 创建 YouTube 播放列表

    我正在尝试使用 NodeJS 服务器创建 YouTube 播放列表 我已按照 Oauth 的 NodeJS 快速入门说明进行操作 如以下链接所示 https github com youtube api samples blob maste
  • 使用 CSP 防止自动点击链接 XSS 攻击

    当将 CSP 用于稍微不同的目的 沙箱 时 我意识到一个非常简单的自动点击链接似乎甚至可以绕过相对严格的 CSP 我所描述的内容如下 内容安全政策 default src none script src unsafe inline 还有身体
  • 如何在 JavaScript 中将日期时间微格式转换为本地时间?

    我有一个页面当前正在使用日期时间微格式 http microformats org wiki datetime design pattern显示时间戳 但我只显示我自己的时区的人类可读时间
  • 调用一个从 AngularJS 表达式本地计算值的函数是不是很糟糕?

    我读了关于使用范围的一些 AngularJS 陷阱的文章 http thenittygritty co angularjs pitfalls using scopes 并且它指出您不应在表达式中使用函数 并且我知道每次框架认为需要时都可能会

随机推荐

  • Qt5.14.2-windows平台配置Qt Creator的ARM体系结构Linux交叉编译器

    1 安装Qt并下载对应源码 下载并安装Qt 下载对应的源码包 windows版本选择zip格式的源码包 Index of archive qt 5 14 5 14 2https download qt io archive qt 5 14
  • LLM本地知识库问答系统(二):如何正确使用LlamaIndex索引

    推荐阅读列表 LLM本地知识库问答系统 一 使用LangChain和LlamaIndex从零构建PDF聊天机器人指南 上一篇文章我们介绍了使用LlamaIndex构建PDF聊天机器人 本文将介绍一下LlamaIndex的基本概念和原理 Ll
  • Markdown语法插入代码

    方法1 1 插入单行代码 code class EnlighterJSRAW 代码内容 code 2 插入多行代码 code class EnlighterJSRAW code code class EnlighterJSRAW 我是代码内
  • el-select结合el-tree实现树形多选

    形式一 业务需求 1 在树形列表中选择项目 2 树形列表需带有筛选过滤功能 3 将已选择的项目展示在输入框中 4 输入框中的选中项可以被单独快速删除 5 下拉框中可再次对之前选择过的项目进行重新选择 实现思路 1 所用技术 vue elem
  • Android 应用内打开Word、Excel、PPT、PDF等文档

    Android平台中 可以使用以下几种方式打开Word和Excel文档 预览图 一 直接上传给第三方之后用webview打开 1 微软 https view officeapps live com op view aspx src 文件链接
  • 编程练习题——用FileReader读取文件内容

    题目目标 main 方法的第一个参数是文件名 指定文件的每个行包含下列各式 名称 月 日 年 其中 姓名 是 String 可以包含空格分隔的数个字词 月 为 int 日 为 int 年 为 int 数据由空格分隔 使用文件数据填写 PEO
  • 02-百度翻译逆向

    1 分析 发现每次请求只有sign不一样 其他的都一样 2 在这个里面找sing 3 可以看到是在b函数中做的手脚 点进去b里面看看 4 直接把这个段复制下 在python中执行js代码 因为下面要使用e n函数使用 e n函数也复制 5
  • 数据库操作--增删改查

    一 数据操作语句 DDL 数据定义语言 定义数据的结构 列 CREATE DROP ALTER TRUNCATE DML 数据操作语言 操作数据的记录行 INSERT DELETE UPDATE DQL 数据查询语言 对数据没有影响 SEL
  • error C2039: “tag”: 不是“boost::Q_FOREACH”的成员 (XXX.cpp) 问题

    记一个编译问题 错误 252 error C2039 tag 不是 boost Q FOREACH 的成员 XXX cpp VS上编译项目时出现一下错误 VS上报错的截图 导致的原因 我在头文件和 cpp文件上包含了同一个
  • linux下前端vue+后端vue+mysql+redis+springboot+nginx的docker部署记录(nginx下多个vue前端)

    1 服务器 推荐购买腾讯云的学生优惠25岁以下免验证 我购买的是轻量应用服务器 5M宽度每月1000G不香吗 系统是CentOS 7 6 购买好服务器并通过Xshell或其他软件连接好 就是前期的准备了 当然除了项目 自备 2 安装dock
  • 麻雀键值数据库 1.0.0版本

    2023年5月26日 周五下午 这个版本主要实现了设置键值对和读取键值对 下个版本主要实现把内存中的数据写入到磁盘中 把磁盘中的数据读取到内存中 这个项目我已经放到了github上 GitHub JuLongZhiLu MaQueKVDB
  • 问题解决——Python中出现“ModuleNotFoundError: No module named 'utils'”

    出现问题 ModuleNotFoundError No module named utils 解决方法 python2安装 pip install web py python3安装 pip install web py 0 40 dev1
  • qt打印html边距设置无效,如何设置QTextDocument边距和其他属性(setHTML,print to pdf)?...

    我有以下证书类用于生成一些图像和数据的pdf文档 设置图像源后 我调用generate 函数并获取test pdf输出文件 该文档是使用setHtml html 方法基于QTextDocument类创建的 问题是我在文档周围有很大的空白区域
  • Hive中如何定位数据倾斜

    1 概述 在hive中执行sql任务时 当任务在reduce阶段一直卡在99 时 很有可能出现了数据倾斜 这个时候如果我们 的sql很长 需要判断出是哪段sql导致的数据倾斜 才便于我们解决问题 2 定位数据倾斜 下面以一条sql为例子 记
  • 牛客七夕赛 D.拜托了,牛老师

    题意 给定 n n n 将 n n n分解成 k k k个不同因数的乘积 问
  • 音视频开发开发核心知识+新手入门必看基础知识

    音视频开发是一个广泛的领域 它涉及到多个技术领域 包括音频编解码 视频编解码 媒体容器格式 流媒体传输 音视频处理等 以下是音视频开发的一些基础知识 音频编解码器 音频编解码器是将数字音频信号编码成一种压缩格式 并且能够解码压缩的音频数据以
  • 错误:找不到或无法加载主类

    可能存在的问题 类路径错误 请确保你的IDE或构建工具正确配置了项目的类路径 并且能够找到 com coll ApiApplication 类所在的位置 可以检查项目的配置文件 如pom xml 或类路径设置 确保类被正确地包含在项目中 编
  • 【转】Configuring VM Acceleration on Linux

    Configuring VM Acceleration on Linux Linux based systems support virtual machine acceleration through the KVM software p
  • texlive下载速度慢(完美解决)

    在使用latex之前 都需要下载texlive 这个过程十分煎熬 官网下载 Index of CTAN systems texlive Images bjtu edu cn 清华源 Index of CTAN systems texlive
  • nodejs koa

    第一步 如何写一个基于node的hello world 创建新的文件夹nodejsdemo 在cmd命令行中进入新建的文件夹nodejsdemo 运行 npm init y 生成package json文件 在cmd命令行中进入新建的文件夹