前后端交互—开发一个完整的服务器

代码下载

初始化

新建 apiServer 文件夹作为项目根目录,并在项目根目录中运行如下的命令,初始化包管理配置文件:

npm init -y

运行如下的命令,安装 express、cors:

npm i express cors

在项目根目录中新建 app.js 作为整个项目的入口文件,配置 cors 跨域中间件、解析表单数据的中间件,并启动服务器:

// 导入 express 模块
const express = require('express');
// 创建 express 的服务器实例
const app = express();// 配置跨域中间件
const cors = require('cors');
app.use(cors());// 配置解析 application/x-www-form-urlencoded 格式的表单数据的中间件
app.use(express.urlencoded({ extended: false }));// 指定端口号并启动web服务器
app.listen('80', function() {console.log('server running at http://127.0.0.1:80');
});

因为在处理函数中,需要多次调用 res.send() 向客户端响应 处理失败 的结果,为了简化代码, 可以手动封装一个 res.cc() 函数。在 app.js 中所有路由之前,声明一个全局中间件,为 res 对象挂载一个 函数 :

// 在 路由 之前自定义中间件,处理错误的响应数据
app.use(function(req, res, next) {// status 默认值1,表示失败,0表示成功// err 可能是错误对象,也可能是错误描述字符串res.cc = function(err, status = 1) {res.send({status: status,message: err instanceof Error ? err.message : err});};next();
});
  1. 在项目根目录中,新建 router 文件夹,用来存放所有的 路由 模块,路由模块中,只存放客户端的请求与处理函数之间的映射关系
  2. 在项目根目录中,新建 routerHandler 文件夹,用来存放所有的 路由处理函数模块 路由处理函数模块中,专门负责存放每个路由对应的处理函数
  3. 在项目根目录中,新建 db 文件夹,用来存放所有的 数据库 模块
  4. 在项目根目录中,新建 middleware 文件夹,用来存放所有的 自定义中间件 模块

登录注册

新建 users 表,在 my_db_01 数据库中,新建 ev_users 表如下:
请添加图片描述

安装并配置 mysql 模块,需要安装并配置 mysql 这个第三方模块,来连接和操作 MySQL 数据库,运行如下命令,安装 mysql 模块:

npm i mysql

在项目根目录中 db 文件夹新建 index.js 文件,在此自定义模块中创建数据库的连接对象:

const mysql = require('mysql');
const db = mysql.createPool({host: 'localhost',port: '3306',user: 'root',password: 'admin123',database: 'my_db_01'
});module.exports = db;

初始化用户路由模块

在 router 文件夹中,新建 user.js 文件,作为用户的路由模块,并初始化代码如下:

const express = require('express') // 创建路由对象
const router = express.Router()
// 注册新用户
router.post('/signin', (req, res) => {res.send('signin OK')
})
// 登录
router.post('/login', (req, res) => {res.send('login OK')
})
// 将路由对象共享出去 module.exports = router

在 app.js 中,导入并使用 用户路由模块 :

// 配置路由
const user = require('./router/user');
app.use('/api', user);

抽离用户路由模块中的处理函数

为了保证 路由模块 的纯粹性,所有的 路由处理函数,必须抽离到对应的 路由处理函数模块中。

在 /routerHandler/user.js 中,使用 exports 对象,分别向外共享如下两个 路由处理函数:

/**
* 在这里定义和用户相关的路由处理函数,供 /router/user.js 模块进行调用 */// 注册用户的处理函数 
exports.signin = (req, res) => {res.send('signin OK')
}// 登录的处理函数
exports.login = (req, res) => {res.send('login OK')
}

将 /router/user.js 中的代码修改为如下结构:

const express = require('express');
const router = express.Router();
const handler = require('../routerHandler/user');// 注册
router.post('/signin', handler.signin);// 登录
router.post('/login', handler.login);module.exports = router;

joi 表单数据验证

表单验证的原则:前端验证为辅,后端验证为主,后端 永远不要相信 前端提交过来的 任何内容。

在实际开发中,前后端都需要对表单的数据进行合法性的验证,而且, 后端做为数据合法性验证的最后 一个关口 ,在拦截非法数据方面,起到了至关重要的作用。

单纯的使用 if…else… 的形式对数据合法性进行验证,效率低下、出错率高、维护性差。因此, 推荐使用 第三方数据验证模块 ,来降低出错率、提高验证的效率与可维护性, 让后端程序员把更多的精力放在核心业务逻辑的处理上。

安装 joi 包,为表单中携带的每个数据项,定义验证规则:

npm install joi

在 /middleware/expressJoi.js 中,使用 joi 对象,定义并向外共享表单数据验证中间件:

const joi = require('joi');const expressJoi = function (schemas, options = { strict: false }) {// 自定义校验选项// strict 自定义属性,默认不开启严格模式,会过滤掉那些未定义的参数项//        如果用户指定了 strict 的值为 true,则开启严格模式,此时不会过滤掉那些未定义的参数项if (!options.strict) {// allowUnknown 允许提交未定义的参数项// stripUnknown 过滤掉那些未定义的参数项options = { allowUnknown: true, stripUnknown: true, ...options }}// 从 options 配置对象中,删除自定义的 strict 属性delete options.strict// TODO: 用户指定了什么 schema,就应该校验什么样的数据return function (req, res, next) {['body', 'query', 'params'].forEach(key => {// 如果当前循环的这一项 schema 没有提供,则不执行对应的校验if (!schemas[key]) return// 执行校验const schema = joi.object(schemas[key])const { error, value } = schema.validate(req[key], options)if (error) {console.log('---------------');// 校验失败throw error} else {// 校验成功,把校验的结果重新赋值到 req 对应的 key 上req[key] = value}})// 校验通过next()}
}module.exports = expressJoi

新建 /schema/user.js 用户信息验证规则模块,并初始化代码如下:

// @hapi/joi 包,为表单中携带的每个数据项,定义验证规则
const joi = require('joi');// * string() 值必须是字符串
// * alphanum() 值只能是包含 a-zA-Z0-9 的字符串 * min(length) 最小长度
// * max(length) 最大长度
// * required() 值是必填项,不能为 undefined
// * pattern(正则表达式) 值必须符合正则表达式的规则
const username = joi.string().alphanum().min(2).max(20).required();
const password = joi.string().required().pattern(/^[\S]{6,16}$/);exports.schema = {user: {body: {username,password}}
}

修改 /router/user.js 中的代码如下:

// 导入验证表单数据的中间件
const expressJoi = require('../middleware/expressJoi');
// 导入需要的验证规则对象
const { schema } = require('../schema/user');// 为 注册新用户 配置表单验证中间件
// 在注册新用户的路由中,声明局部中间件,对当前请求中携带的数据进行验证
//      数据验证通过后,会把这次请求流转给后面的路由处理函数
//      数据验证失败后,终止后续代码的执行,并抛出一个全局的 Error 错误,进入全局错误级别中间件中进行 处理
router.post('/signin', expressJoi(schema.user), handler.signin);// 登录
router.post('/login', expressJoi(schema.user), handler.login);

在 app.js 的全局错误级别中间件中,捕获验证失败的错误,并把验证失败的结果响应给客户端:

// 配置 错误处理 中间件
const joi = require('joi');
app.use(function(err, req, res, next) {// console.log(err);if (err instanceof joi.ValidationError) {// 处理数据校验错误} else {// 处理其他未知错误}return res.cc(err);
});

加密处理

为了保证密码的安全性,不建议在数据库以 明文 的形式保存用户密码,推荐对密码进行 加密存储。使用 对用户密码进行加密,优点:

  • 加密之后的密码, 无法被逆向破解
  • 同一明文密码多次加密,得到的 加密结果各不相同 ,保证了安全性

运行如下命令,安装 bcryptjs:

npm i bcryptjs

调用 bcrypt.hashSync(明文密码, 随机盐的长度) 方法,对用户的密码进行加密处理:

const bcrypt = require('bcryptjs');
// 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串
userinfo.password = bcrypt.hashSync(userinfo.password, 10);

调用 bcrypt.compareSync(用户提交的密码, 数据库中的密码) 方法比较密码是 否一致,返回值是布尔值(true 一致、false 不一致):

// 拿着用户输入的密码,和数据库中存储的密码进行对比
const compareResult = bcrypt.compareSync(userinfo.password, results[0].password);// 如果对比的结果等于 false, 则证明用户输入的密码错误 
if (!compareResult) {console.log('密码错误!');
}

注册

  1. 检测表单数据是否合法
  2. 检测用户名是否被占用
  3. 对密码进行加密处理
  4. 插入新用户

新建 /middleware/constValue.js 常量数据模块,并初始化代码如下:

// sql 语句
// 查找用户名
const selectUN = `select * from users where username = ?`;
// 插入用户
const insertUser = `insert into users set ?`;module.exports = {selectUN,insertUser
}

修改 /routerHandler/user.js 中的代码,实现最终完整的登录逻辑,如下:

const bcrypt = require('bcryptjs');
const constValue = require('../middleware/constValue');
const signin = (req, res) => {const info = req.body;// 数据校验交给 expressJoi 中间件处理// if (info.username && info.password) {db.query(constValue.selectUN, info.username, function(err, result) {if (err) {res.cc(err);} else if (result.length > 0) {res.cc('用户名被占用,请更换其他用户名!');} else {// 调用 bcrypt.hashSync(明文密码, 随机盐的长度) 方法,对用户的密码进行加密处理info.password = bcrypt.hashSync(info.password, 10);db.query(constValue.insertUser, {username: info.username, password: info.password}, function(err, result) {if (err) {res.cc(err);} else if (result.affectedRows === 1) {res.cc('注册成功!', 0);} else {res.cc('注册失败,请稍后重试!');}});}});// } else {//     res.cc('用户名或密码不能为空!');// }
};

登录

  1. 检测表单数据是否合法
  2. 根据用户名查询用户的数据
  3. 判断用户输入的密码是否正确
  4. 生成 JWT 的 Token 字符串

在 /middleware/constValue.js 定义并导出查询用户数据的 SQL 语句以及 JWT 秘钥、加密算法、无权限表达式:

// 查找用户信息
const selectInfo = `select id, username, nickname, email, user_pic from users where id = ?`;
// JWT 秘钥
const jwtSecretKey = 'JWTSecretKeyabcABC123!@#JWTSecretKey';
// JWT 加密算法
const jwtAlgorithms = 'HS256';
// JWT 无需权限的接口 表达式
const jwtUnlessPath = /^\/api\//;

运行如下的命令,安装生成 Token 字符串的包、解析 Token 的中间件的包:

npm i jsonwebtoke express-jwt

核心注意点: 在生成 Token 字符串的时候,一定要剔除 密码 和 头像 的值 通过 ES6 的高级语法,快速剔除 密码 和 头像 的值,将用户信息对象加密成 Token 字符串:

// 通过 ES6 的高级语法,快速剔除用户敏感信息后的数据作为 token 的加密数据
const user = { ...result[0], password: '', user_pic: '' }
// token 有效期为 10 天
const token = jwt.sign(user, constValue.jwtSecretKey, { expiresIn: '10d' });

在 app.js 中注册路由之前,配置解析 Token 的中间件:

// 配置 解析 Token 的中间件
var { expressjwt } = require('express-jwt');
const constValue = require('./middleware/constValue');
app.use(expressjwt({secret: constValue.jwtSecretKey, algorithms: [constValue.jwtAlgorithms]
}).unless({path: [constValue.jwtUnlessPath]
}));

在 app.js 中的 错误级别中间件里面,捕获并处理 Token 认证失败后的错误:

app.use(function(err, req, res, next) {// console.log(err);if (err instanceof joi.ValidationError) {// 处理数据校验错误} else if (err.name === 'UnauthorizedError') {// 处理身份认证失败的错误return res.cc('身份验证失败!');} else {// 处理其他未知错误}return res.cc(err);
});

修改 /routerHandler/user.js 中的代码,实现最终完整的登录逻辑,如下:

const jwt = require('jsonwebtoken');
const expressJwt = require('express-jwt');
const { use } = require('../router/user');
const login = (req, res) => {const info = req.body;// 查询 username 是否存在db.query(constValue.selectUN, info.username, function(err, result) {if (err) {res.cc(err);} else if (result.length === 1) {// 判断密码是否正确if (bcrypt.compareSync(info.password, result[0].password)) {// 剔除用户敏感信息后的数据作为 token 的加密数据const user = { ...result[0], password: '', user_pic: '' }console.log(user);const token = jwt.sign(user, constValue.jwtSecretKey, { expiresIn: '10d' });res.send({status: 0,token: 'Bearer ' + token,message: '登录成功!'});} else {res.cc('密码错误!');}} else {res.cc('登录失败!');}});
};

个人中心

获取用户的基本信息

  1. 初始化 路由 模块
  2. 初始化 路由处理函数 模块
  3. 获取用户的基本信息

创建 /router/userinfo.js 路由模块,并初始化如下的代码结构:

const express = require('express');
const router = express.Router();
const handler = require('../routerHandler/userinfo');// 获取用户信息
router.get('/userinfo', handler.userinfo);module.exports = router;

在 app.js 中导入并使用个人中心的路由模块:

const userinfo = require('./router/userinfo');
app.use('/my', userinfo);

在 /middleware/constValue.js 定义并导出查询用户信息的 SQL 语句:

// 查找用户信息
const selectInfo = `select id, username, nickname, email, user_pic from users where id = ?`;

创建 /routerHandler/userinfo.js 文件,实现最终完整的逻辑,如下:

const db = require('../db/index');
const constValue = require('../middleware/constValue');const userinfo = function(req, res) {console.log(req.auth);db.query(constValue.selectInfo, req.auth.id, function(err, result) {if (err) {res.cc(err);} else if (result.length === 1) {res.send({status: 0,data: result[0],message: '用户信息获取成功!'});} else {res.cc('用户信息获取失败!');}});
};module.exports = {userinfo
};

更新用户的基本信息

  1. 定义路由和处理函数
  2. 验证表单数据
  3. 实现更新用户基本信息的功能

具体实现见代码……

重置密码

  1. 定义路由和处理函数
  2. 验证表单数据
  3. 实现重置密码的功能

在 /schema/user.js 验证规则模块中,定义 newPassword 的验证规则并使用 exports 向外共享如下的验证规则对象:

const password = joi.string().required().pattern(/^[\S]{6,16}$/);
// 1. joi.ref('oldPassword') 表示 newPassword 的值必须和 oldPassword 的值保持一致
// 2. joi.not(joi.ref('oldPwd')) 表示 newPwd 的值不能等于 oldPwd 的值
// 3. .concat() 用于合并 joi.not(joi.ref('oldPwd')) 和 password 这两条验证规则
const newPassword = joi.not(joi.ref('oldPassword')).concat(password);exports.schema = {updatePassword: {body: {oldPassword: password,newPassword}}
}

其他具体实现见代码……

更新用户头像

  1. 定义路由和处理函数
  2. 验证表单数据
  3. 实现更新用户头像的功能

在 /schema/user.js 验证规则模块中,定义 user_pic 的验证规则并使用 exports 向外共享如下的验证规则对象:

// dataUri() 指的是如下格式的字符串数据:
// data:image/png;base64,VE9PTUFOWVNFQ1JFVFM=
const user_pic = joi.string().dataUri().required();exports.schema = {updateAvatar: {body: {user_pic}}
}

在 /router/userinfo.js 模块中,导入需要的验证规则对象, 修改 更新用户头像 的路由:

const expressJoi = require('../middleware/expressJoi');
const { schema } = require('../schema/user');
// 更换头像
router.post('/updateAvatar', expressJoi(schema.updateAvatar), handler.updateAvatar);

在 /middleware/constValue.js 定义并导出 更新用户头像 的 SQL 语句:

// 修改头像
const updateAvatar = `update users set user_pic = ? where id = ?`;

处理 /routerHandler/userinfo.js 中的代码,实现最终完整的登录逻辑,如下:

// 更换头像
const updateAvatar = (req, res) => {console.log('------------');console.log(req.body);db.query(constValue.updateAvatar, [req.body.user_pic, req.auth.id], (err, result) => {if (err) {res.cc(err);} else if (result.affectedRows === 1) {res.cc('更换头像成功!', 0);} else {res.cc('更换头像失败!');}})
}

文章分类管理

具体实现见代码……

文章管理

新建 articles 表如下:
请添加图片描述

发布新文章

  1. 初始化路由模块
  2. 初始化路由处理函数模块
  3. 使用 multer 解析表单数据
  4. 验证表单数据
  5. 实现发布文章的功能

创建 /routerHandler/article.js 路由处理函数模块,并初始化如下的代码结构:

// 发布新文章的处理函数 
exports.addArticle = (req, res) => {res.send('ok')
}

创建 /router/article.js 路由模块,并初始化如下的代码结构:

// 导入 express
const express = require('express') 
// 创建路由对象
const router = express.Router()// 导入文章的路由处理函数模块
const handler = require('../routerHandler/article');// 发布新文章
routor.post('/addArticle', handler.addArticle);// 向外共享路由对象 
module.exports = router

在 app.js 中导入并使用文章的路由模块:

const article = require('./router/article');
app.use('/my', article);
使用 multer 解析表单数据

注意: 使用 express.urlencoded() 中间件无法解析 multipart/form-data 格式的请求体 数据。

推荐使用 multer 来解析 multipart/form-data 格式的表单数据。运行如下的终端命令,在项目中安装 multer :

npm i multer

在 /router/article.js 模块中导入并配置 multer :

// 导入解析 formdata 格式表单数据的包 
const multer = require('multer') 
// 导入处理路径的核心模块
const path = require('path')
// 创建 multer 的实例对象,通过 dest 属性指定文件的存放路径
const upload = multer({ dest: path.join(__dirname, '../uploads') })// 发布新文章的路由
// upload.single() 是一个局部生效的中间件,用来解析 FormData 格式的表单数据
// 将文件类型的数据,解析并挂载到 req.file 属性中
// 将文本类型的数据,解析并挂载到 req.body 属性中
routor.post('/addArticle', uploads.single('cover_img'), handler.addArticle);
实现发布文章的功能

通过 express-joi 自动验证 req.body 中的文本数据;通过 if 判断手动验证 req.file 中的 文件数据。

在 /schema/user.js 验证规则模块中,定义数据验证规则并使用 exports 向外共享如下的 验证规则对象:

const id = joi.number().integer().min(1).required();
const title = joi.string().required();
const content = joi.string().required().allow('');
const state = joi.string().valid('已发布', '草稿').required();exports.schema = {addArticle: {body: {title,content,cate_id: id,state}}
}

在 /router/article.js 模块中,导入需要的验证规则对象,并在路由中使用。

在 /middleware/constValue.js 定义并导出 发布文章 的 SQL 语句:

// 插入文章
const insertArticle = `insert into articles set ?`;

修改 /routerHandler/article.js 中的代码,实现最终完整的登录逻辑,如下:

const addArticle = (req, res) => {console.log(req.body);console.log(req.file);if (req.file && req.file.fieldname === 'cover_img') {const article = {...req.body,// 文章封面在服务器端的存放路径cover_img: `/uploads/${req.file.filename}`,pub_date: new Date(),author_id: req.auth.id};db.query(constValue.insertArticle, article, (err, result) => {if (err) {res.cc(err);} else if (result.affectedRows === 1) {res.cc('文章添加成功!', 0);} else {res.cc('文章添加失败!');}});} else {res.cc('cover_img 是必选参数!');}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/226036.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

12V升18V4A同步升压恒压WT3210

12V升18V4A同步升压恒压WT3210 WT3210 是一款高功率密度的全集成同步升压转换器,内部集成的功率MOSFET管导通电阻为上管8mΩ和下管15mΩ。可为便携式系统提供空间小尺寸 解决方案。WT3210具有 2.7V 至 20V 的宽输入电压范围,应用在单节或两节锂电池的便携…

关于嵌入式开发的一些信息汇总:嵌入式C开发人员、嵌入式系统Linux

关于嵌入式开发的一些信息汇总:嵌入式C开发人员、嵌入式系统Linux 1 关于嵌入式 C 开发人员1.1 嵌入式 C 开发人员必须具备的一些基本技能是:1.2 嵌入式C开发的应用案例 2 如何学习用于嵌入式系统的 Linux2.1 如何学习Linux2.1.1 第一步:创建…

漏刻有时百度地图API实战开发(12)(切片工具的使用、添加自定义图层TileLayer)

TileLayer向地图中添加自定义图层 var tileLayer new BMap.TileLayer();tileLayer.getTilesUrl function (tileCoord, zoom) {var x tileCoord.x;var y tileCoord.y;return images/tiles/ zoom /tile- x _ y .png;}var lockMap new BMap.MapType(lock_map, tileLaye…

【MySQL】MySQL 在 Centos 7环境安装教程

文章目录 1.卸载不要的环境2.检查系统安装包3.获取mysql官方yum源4.安装mysql yum 源,对比前后yum源5.安装mysql服务6.查看配置文件和数据存储位置7.启动服务和查看启动服务8.登录9.配置my.cnf 1.卸载不要的环境 先检查是否有mariadb存在 ps ajx |grep mariadb如果…

C语言定长数组 变长数组 柔性数组

C语言定长数组 变长数组 柔性数组 文章目录 C语言定长数组 变长数组 柔性数组1. 定长数组2. 变长数组3. 柔性数组3.1 结构体的大小3.2 柔性数组的使用 1. 定长数组 在C99标准之前,C语言在创建数组的时候,数组的大小只能使用常量,常量表达式来…

【EI会议征稿】第三届电子信息工程、大数据与计算机技术国际学术会议(EIBDCT 2024)

第三届电子信息工程、大数据与计算机技术国际学术会议(EIBDCT 2024) 2024 3rd International Conference on Electronic Information Engineering, Big Data and Computer Technology 第三届电子信息工程、大数据与计算机技术国际学术会议(…

小狐狸GPT付费2.4.9弹窗版学习源码介绍

小狐狸GPT付费2.4.9弹窗版学习源码是一套基于GPT(Generative Pre-trained Transformer)模型的开源代码库,旨在帮助开发者快速构建和训练自己的语言模型。该源码集成了多个先进的自然语言处理技术,包括预训练、微调、对话生成等&am…

云服务领取证书,注册域名,实现nginx服务配置证书

目录 1.登录网址腾讯云 2.腾讯云注册域名 3.实名认证,上传信息 4.域名注册 5.领取证书 6.域名与证书绑定 7.下载证书 8.设置环境 9.域名解析 10. 本地域名解析 11.上传证书到目录 12.nginx配置文件做地址重写到证书域名 13.配置证书conf 14.设置index…

磁盘坏道修复工具-是一款非常方便实用的磁盘坏道修复软件-供大家学习研究参考

1、支持磁盘数据擦除。 2、杜绝因硬盘坏道,而产生个人隐私数据泄露的问题。 3、支持对该磁盘格式化。 下载:https://download.csdn.net/download/weixin_43097956/88625682

关于“Python”的核心知识点整理大全22

目录 ​编辑 9.4.2 在一个模块中存储多个类 虽然同一个模块中的类之间应存在某种相关性,但可根据需要在一个模块中存储任意数量的 类。类Battery和ElectricCar都可帮助模拟汽车,因此下面将它们都加入模块car.py中: car.py my_electric_car…

基于 Gin 的 HTTP 代理上网行为记录 demo

前言: 前端时间写了好几篇使用 Gin 框架来做 HTTP 代理 demo 的文章,然后就想着做一个记录上网行为的小工具,就是简单记录看看平时访问了什么网站(基于隧道代理的,不是中间人代理,所以只能记录去了哪里,不能…

wps左上角有绿色小三角的数字如何求和

1.这个状态是求和不了的,使用求和公式求出来的也是0 2.进行如下操作 3.转换好后 则可以求和成功了

深度学习 Day16——P5运动鞋识别

🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 | 接辅导、项目定制 文章目录 前言1 我的环境2 代码实现与执行结果2.1 前期准备2.1.1 引入库2.1.2 设置GPU(如果设备上支持GPU就使用GPU,否则使用C…

【Qt QML 入门】TextArea

TextArea也是一个多行文本编辑器。TextArea相比texttedit,增加了占位符文本,并添加了样式定义。 import QtQuick import QtQuick.Window import QtQuick.ControlsWindow {id: winwidth: 800height: 600visible: trueTextArea {id: taanchors.centerIn: …

解决Visual Studio 各版本都出现新建项目后解决方案下没有文件和项目问题

一步一步创建C#控制台应用程序也会出错,这个你可能不会相信,我就遇到了这么一次,就在刚刚,是的,我都不敢相信,用了这么多年的新建一个控制台程序居然不正常了。新建完毕发现里面什么都没有,除了…

首发卡密引流系统源码

程序特色: 支持个人和企业小程序广告获取卡密。 支持短视频点赞和关注获取卡密。 搭建教程: 环境要求:Nginx、MySQL 5.6、PHP 5.6 步骤: 将压缩包解压至网站根目录。 打开域名/install,按照提示填写数据库信息进行…

JS对象循环引用的危害:你知道吗?

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…

移动端机器学习框架 MDL 简介与实践

Mobile-deep-learning(MDL) MDL 是百度研发的可以部署在移动端的基于卷积神经网络实现的移动端框架,可以应用在图像识别领域。 具体应用:在手机百度 App 中,用户只需要点击自动拍开关,将手机对准物体&…

联想王传东:AI PC迈入AI Ready 即将开启AI On

“AI PC已经正式迈入AI Ready 阶段,接下来会逐渐进入到AI On阶段。”12月16日,英特尔人工智能创新应用大赛启动仪式在深圳举办。作为独家AI PC合作伙伴,联想集团副总裁、中国区首席市场官王传东代表公司出席仪式并致辞。 王传东认为AI PC的发…

大模型Transformer 推理 :kvCache原理浅析

大模型Transformer 推理 :kvCache原理浅析 kvCache 原理 在采样时,Transformer模型会以给定的提示/上下文作为初始输入进行推理(可以并行处理),然后逐一生成额外的标记来继续完善生成的序列(体现了模型的自回归性质)。在采样过程中,Transformer会执行自注意力操作,为…