Gin框架快速入门实战

news2024/9/27 21:19:08

gin 框架快速入门

工具

测试工具:

  • 浏览器拓展 postwoman
  • apipost

gin路由,gin 程序的热加载

gin官网文档: https://gin-gonic.com/zh-cn/docs/

fresh安装 : go get github.com/pilu/fresh

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

// gin 程序的热加载, gin路由  get  post  put  delete
func main() {
	//创建一个默认的路由引擎
	r := gin.Default()
	//配置路由
	r.GET("/", func(c *gin.Context) {
		c.String(200, "你好gin")
	})

	r.GET("/news", func(c *gin.Context) {
		//http.StatusOK go  自带  http包中的常量  表示200  状态码
		c.String(http.StatusOK, "这是%v", "新闻页面")
	})

	//post 请求
	r.POST("/post", func(c *gin.Context) {
		c.String(200, "这是一个post请求")

	})

	//put请求
	r.PUT("/put", func(c *gin.Context) {
		c.String(200, "这是一个put数据主要用于编辑数据")

	})

	r.DELETE("/delete", func(c *gin.Context) {
		c.String(200, "这是一个delete请求主要用于删除数据")

	})

	//启动 服务 , 默认8080端口
	r.Run()
}

gin 路由中响应数据

注意: 配置模板再goland 中默认的 是从项目根路径开始查找的

修改位置为:
在这里插入图片描述

go 代码

// gin 路由响应数据  string,   json ,  jsonp , html
func ginDemo02() {
	type Article struct {
		//`json:"title"`  将前面大写的自动替换为后面小写的
		Title   string `json:"title"`
		Desc    string `json:"desc"`
		Content string `json:"content"`
	}
	//创建路由引擎
	r := gin.Default()
	//加载模板 注意:  默认是以 项目 为根目录,
	r.LoadHTMLGlob("templates/*")
	r.GET("/json", func(c *gin.Context) {
		c.JSON(200, map[string]interface{}{
			"success": true,
			"message": "你好gin",
		})

	})

	r.GET("/json2", func(c *gin.Context) {
		//gin.H  等价于 type H map[string]any
		c.JSON(200, gin.H{
			"success": true,
			"message": "你好gin gin.H",
		})

	})

	//返回自定义结构体
	r.GET("/json3", func(c *gin.Context) {
		//gin.H  等价于 type H map[string]any
		a := &Article{
			Title:   "这是一个json请求",
			Desc:    "描述",
			Content: "内容",
		}
		c.JSON(200, a)
	})

	//jsonp 请求  主要用于跨域请求
	//加上回调函数callback: http://127.0.0.1:8080/jsonp?callback=xxx
	//xxx({"title":"这是一个jsonp请求","desc":"描述","content":"内容"});
	r.GET("/jsonp", func(c *gin.Context) {
		//gin.H  等价于 type H map[string]any
		a := &Article{
			Title:   "这是一个jsonp请求",
			Desc:    "描述",
			Content: "内容",
		}
		c.JSONP(200, a)
	})

	//返回XML数据
	r.GET("/xml", func(c *gin.Context) {
		//gin.H  等价于 type H map[string]any
		a := &Article{
			Title:   "这是一个xml请求",
			Desc:    "描述",
			Content: "内容",
		}
		c.XML(200, a)
	})

	//响应 html数据
	r.GET("/news", func(c *gin.Context) {
		c.HTML(200, "goods.html", gin.H{
			"title": "这是商品页面",
		})
	})
	r.Run()
}

html模板文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>goods</title>
</head>
<body>


<!--
	r.GET("/news", func(c *gin.Context) {
		c.HTML(200, "goods.html", gin.H{
			"title": "这是商品页面",
		})
	})
-->
<h2>{{.title}}</h2>
</body>
</html>

gin 模板渲染

go 代码


// gin   html  模板渲染
func gindemo03() {
	r := gin.Default()
	//**  表示文件夹
	r.LoadHTMLGlob("templates/**/*")

	//注意页面的名字 为 模板文件 define 的名字
	//{{ define "admin/news.html" }}

	r.GET("/", func(c *gin.Context) {
		c.HTML(200, "default/index.html", gin.H{
			"title":          "hello  gin",
			"score":          92,
			"hobby":          []string{"吃饭", "睡觉", "coding"},
			"testEmptySlice": []string{},
			"news": &Article{
				Title:   "新闻标题",
				Desc:    "描述",
				Content: "with结构结构体使用",
			},
		})
	})

	r.GET("/news", func(c *gin.Context) {
		c.HTML(http.StatusOK, "default/news.html", gin.H{
			"title": "hello 前台新闻页面",
		})

	})

	r.GET("/admin", func(c *gin.Context) {
		c.HTML(http.StatusOK, "admin/index.html", gin.H{
			"title": "hello  admin",
		})

	})

	r.GET("/admin/news", func(c *gin.Context) {
		c.HTML(http.StatusOK, "admin/news.html", gin.H{
			"title": "hello  admin news ",
		})

	})

	r.Run()
}

html模板文件

<!--给模板配置名称   用来区分不同文件夹下的模板,成对出现-->
{{ define "admin/index.html" }}

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>


{{  .title }}
<h2>这是后台首页</h2>
</body>
</html>

{{ end }}

gin模板的基本语法

演示go代码


	r.GET("/", func(c *gin.Context) {
		c.HTML(200, "default/index.html", gin.H{
			"title":          "hello  gin",
			"score":          92,
			"hobby":          []string{"吃饭", "睡觉", "coding"},
			"testEmptySlice": []string{},
			"news": &Article{
				Title:   "新闻标题",
				Desc:    "描述",
				Content: "with结构结构体使用",
			},
		})
	})

输出数据

{{}} 输出数据

{{.title}}

变量

变量

<!--赋值给一个变量-->
{{ $t := .title }}
<!--输出变量-->
<h4> {{$t}}</h4>

比较函数

比较函数

在这里插入图片描述

条件判断

条件判断

注意 {{end}} 结尾

在这里插入图片描述

range 遍历

range 遍历

<!--循环遍历-->
<ul>
    {{range $key, $value := .hobby}}
    <li>{{$key}} --- {{$value}}</li>
    <!--    如果 没有值走else-->
    {{else}}
    <li>列表为空</li>
    {{end}}

</ul>

with 解构结构体

with 解构结构体

<!--with 结构结构体, 相当于将 news 赋值给  一个 点 -->
{{ with .news}}
{{.Title}}
{{.Desc}}
{{.Content}}
{{end}}

自定义模板函数

模板自带 函数

在这里插入图片描述

自定义模板函数

在utils 啊包中定义一个时间戳转换函数

// 将时间戳转换为 常见时间格式
func UnixToTime(timestamp int) string {
	t := time.Unix(int64(timestamp), 0)
	return t.Format("2006-01-02 15:04:05")
}

注入自定义函数

//自定义模板函数, 注入自己定义的函数, 注意这个 需要在加载模板之前
	r.SetFuncMap(template.FuncMap{
		"UnixToTime": utils.UnixToTime,
	})
	// 加载模板文件 **  表示文件夹
	r.LoadHTMLGlob("templates/**/*")

使用


	r.GET("/", func(c *gin.Context) {
		c.HTML(200, "default/index.html", gin.H{
			"title":          "hello  gin",
			"score":          92,
			"hobby":          []string{"吃饭", "睡觉", "coding"},
			"testEmptySlice": []string{},
			"timeStamp":      2953130242,

		})
	})

在html文件中调用函数

<!--使用模板自带函数函数, 注意是计算 字节大小不是长度-->
<!--.title 相当于 传递的参数-->
{{ len .title}}
<!--调用自定义模板函数-->
{{ UnixToTime .timeStamp }}

嵌套 template

模板文件嵌套其他模板文件

注意后面需要有一个点 ,才能将当前 页面的数据传递到引入 的模板中

<!--引入其他模板-->
<!--注意后面需要有一个点 ,才能将当前 页面的数据传递到引入 的模板中-->
{{ template "public/page_header.html" .}}

引入静态文件

gin 中 html 加载 css js 图片 等静态文件不能直接加载, 需要先配置静态文件目录

// 当访问  /static/*这个 url 的时候。  就会引入这个 ./static 文件夹内的内容, 这样才能正常加载  static 文件夹内的文件
	r.Static("/static", "./static")

可以正常加载对应文件

-- 访问了 /static/*  这个 url  就会触发 从而自动导入文件夹内的内容,
 <link rel="stylesheet"  href="/static/css/base.css">
 <img src="/static/images/node.jpg">
 

不能正常加载对应文件

 <link rel="stylesheet"  href="/aaa/css/base.css">

即使 文件夹内存在 对应文件也不能正常加载,需要配置 , 这样访问 aaa开头的url, 就会加载 当前 aaa 目录内的文件

r.Static("/aaa", "./aaa")

gin 路由传值

get传值

	//get请求传值
	r.GET("/get", func(c *gin.Context) {
		//获取参数的值
		username := c.Query("u")
		age := c.Query("age")
		//没有接收到参数  使用默认值 1
		page := c.DefaultQuery("page", "1")

		c.JSON(http.StatusOK, gin.H{
			"username": username,
			"age":      age,
			"page":     page,
		})
	})

将接收的值绑定到结构体上

 // form  表示 接收form  表单中的数据 
type UserInfo struct {
	Username string `json:"username" form:"username"`
	Password string `json:"password"  form:"password"`
}

	//将传入的数据绑定到结构体上
	r.GET("/getUser", func(c *gin.Context) {
		user := &UserInfo{}
		//将数据解析到结构体
		err := c.ShouldBind(user)
		if err == nil {
			c.JSON(http.StatusOK, user)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{
				"err": err.Error(),
			})
		}

	})

post传值

	// 接收传递的数据
	r.POST("/doAddUser", func(c *gin.Context) {
		//接收数据
		username := c.PostForm("username")
		password := c.PostForm("pwd")
		//没有传入  使用默认值
		age := c.DefaultPostForm("age", "12")
		c.JSON(http.StatusOK, gin.H{
			"code":    true,
			"message": "成功获取数据",
			"u":       username,
			"p":       password,
			"age":     age,
		})
	})

post 传递xml数据解析到结构体


// 将xml中的数据解析到 结构体中
type News struct {
	Title   string `json:"title" xml:"title"`
	Content string `json:"content" xml:"content"`
}

//获取post xml 数据
	r.POST("/xml", func(c *gin.Context) {
	
		news := &News{}
		//获取数据, 返回一个  []byte
		xmlSliceData, _ := c.GetRawData()
		//将数据 解析到 结构体中
		err := xml.Unmarshal(xmlSliceData, news)
		if err == nil {
			c.JSON(http.StatusOK, news)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{
				"err": err.Error(),
			})
		}
	})

xml测试数据


		<?xml version="1.0" encoding="utf-8"?>
			<news>
			<content type="string">张三</content>
			<title  type="string">姓名</title>
			</news>

动态路由

//动态路由传值
	//访问  list/xxx  会替换 id变成  list/xxx  可以用于传值
	r.GET("/list/:id", func(c *gin.Context) {
		//获取传入的值
		param := c.Param("id")
		c.String(200, "%v", param)
	})


gin路由文件抽离

将不同 功能 的 模块进行分组 抽离成单独的go文件, 方便管理和 协同开发

defaultRoutes.go

package routes

import "github.com/gin-gonic/gin"

func DefaultRoutesInit(r *gin.Engine) {
	//进行路由分组,下面设置的url都是 这个的子层级
	defaultRoutes := r.Group("/")
	{
		defaultRoutes.GET("/", func(c *gin.Context) {
			c.String(200, "首页主页面")

		})

		defaultRoutes.GET("/news", func(c *gin.Context) {
			c.String(200, "首页新闻页面")

		})
	}

}

apiRoutes.go

package routes

import "github.com/gin-gonic/gin"

func ApiRoutesInit(r *gin.Engine) {
	//进行路由分组,下面设置的url都是 这个的子层级

	apiRoutes := r.Group("/api")
	{
		apiRoutes.GET("/", func(c *gin.Context) {
			c.String(200, "api首页面")

		})

		apiRoutes.GET("/news", func(c *gin.Context) {
			c.String(200, "api新闻页面")

		})
	}
}

adminRoutes.go

package routes

import "github.com/gin-gonic/gin"

func AdminRoutesInit(r *gin.Engine) {
	//进行路由分组,下面设置的url都是 这个的子层级
	adminRoutes := r.Group("/admin")
	{
		adminRoutes.GET("/", func(c *gin.Context) {
			c.String(200, "后台首页面")

		})

		adminRoutes.GET("/news", func(c *gin.Context) {
			c.String(200, "后天新闻页面")

		})
	}
}

在main 文件中调用


// 路由文件抽离
func ginDemo05() {
	r := gin.Default()
	//自定义模板函数, 注入自己定义的函数, 注意这个 需要在加载模板之前
	//自定义模板函数, 注入自己定义的函数, 注意这个 需要在加载模板之前
	r.SetFuncMap(template.FuncMap{
		"UnixToTime": utils.UnixToTime,
	})
	//加载模板
	r.LoadHTMLGlob("templates/**/*")

	//加载路由页面
	routes.DefaultRoutesInit(r)
	routes.ApiRoutesInit(r)
	routes.AdminRoutesInit(r)

	r.Run()
}

gin 控制器

路由只负责配置路由, 业务代码由控制器实现

目录参考
在这里插入图片描述

UserController.go

package admin

import "github.com/gin-gonic/gin"

// 将其 挂载到结构体上 可以实现继承关系
type UserController struct {
}

func (conn UserController) Index(c *gin.Context) {
	c.String(200, "后台管理用户首页 --  index")
}

func (conn UserController) News(c *gin.Context) {
	c.String(200, "后天管理新闻页面  --  news")
}

进行路由处理

package routes

import (
	"GinProject/src/demo01/controllers/admin"
	"github.com/gin-gonic/gin"
)

func AdminRoutesInit(r *gin.Engine) {
	//进行路由分组,下面设置的url都是 这个的子层级
	adminRoutes := r.Group("/admin")
	{
		adminRoutes.GET("/", admin.UserController{}.Index)

		adminRoutes.GET("/news", admin.UserController{}.News)
	}
}

gin 中间件

中间件: 匹配路由前和匹配路由完成后 执行的一系列操作

一个简单的中间件

func MiddlewareOne(c *gin.Context) {
	fmt.Println("1-我是一个中间件 --  One")
}

	//func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes
	//可以传入 多可回调函数, 中间的回调函数可以理解为一个简单的中间件
	// 从前往后开始执行 回调函数
	r.GET("/mid", MiddlewareOne, func(c *gin.Context) {
		fmt.Println("这是一个首页...")
		c.String(200, "这是一个首页")

	})

gin 启动引擎默认也是 使用了 中间件的

//Default()  默认使用了中间件  engine.Use(Logger(), Recovery())
	//gin.New():  不使用任何中间件
	r := gin.Default()

中间件中的方法

c.Next() : 跳转执行后续的回调方法, 其他回调函数执行完毕,再继续向后执行

func MiddlewareOne(c *gin.Context) {
	
	//执行后续的 回调方法
	c.Next()
	fmt.Println("1-我是一个中间件 --  One")
}

** c.Abort()** : 中止后续回调函数的执行

func MiddlewareTwo(c *gin.Context) {
	fmt.Println("1-我是一个中间件(全局) -- Two")
	c.Abort()
}

使用中间件计算程序执行时间

// 计算程序运行时间中间件
func TimeMiddleware(c *gin.Context) {
	start := time.Now().UnixNano()
	fmt.Println("我是一个 计算执行时间的中间件")
	c.Next()
	end := time.Now().UnixNano()
	fmt.Println("执行时间", end-start)
}

// 应用
	r.GET("/time", TimeMiddleware, func(c *gin.Context) {
		time.Sleep(1 * time.Second)
		c.String(200, "执行时间")

	})

全局中间件

全局中间件 会配置到 每一个路由上

//配置全局中间件, 可以配置多个
	r.Use(MiddlewareTwo)

路由分组中使用中间件


 //法一:
adminRoutes := r.Group("/admin", middlewares.Initmiddlewares)
//法二
 adminRoutes.Use(middlewares.Initmiddlewares)

中间价和控制器之间共享数据

在 中间件中存储数据

//在中间件中将数据存储到  域中
c.Set("username", "ggbo")

在控制器存储数据

注意:取出数据的类型转换

// 在控制器 取出数据
func (conn UserController) Index(c *gin.Context) {
	//中间价和 控制器之间传递数据
	//  返回  any : type any = interface{}  是空接口的类型  需要转换成 具体的类型
	username, _ := c.Get("username")
	fmt.Println(username)

	//类型转换
	s, ok := username.(string)
	if ok {
		c.String(200, "用户:"+s)

	} else {
		c.String(200, "用户获取失败")
	}

}

中间件中使用 go 协程

注意不能直接 在 go 程中使用原有的 Context操作

func Initmiddlewares(c *gin.Context) {
	
	//使用协程 不能直接使用  原有 Context, 需要复制,然后操作复制的Context
	Copycontext := c.Copy()
	//使用 go 程统计日志
	go func() {
		time.Sleep(5 * time.Second)
		fmt.Println("Done!!!" + Copycontext.Request.URL.Path)
	}()
}

gin 中自定义model

关于Model

只是不同 的命名而已, 就是将公共的方法 抽离 出来组成一个工具 库

gin 实现文件上传

单文件上传

form表单

<form action="/fileupload" method="post" enctype="multipart/form-data">
    选择文件1: <input type="file" name="face">
    <input type="submit" value="提交">
</form>

go 代码

	r.POST("/fileupload", func(c *gin.Context) {
		file, err := c.FormFile("face")
		if err == nil {
			dst := path.Join("./static/upload", file.Filename)
			//保存文件
			c.SaveUploadedFile(file, dst)
			c.String(200, "success")
		} else {
			c.String(200, "error")
		}
	})

多文件不同名

html

<form action="/filesupload" method="post" enctype="multipart/form-data">
    选择文件1: <input type="file" name="face1">
    选择文件2: <input type="file" name="face2">
    <input type="submit" value="提交">
</form>

go 代码

//参考  单文件上传,  其他的相同, 分别获取 不同的文件即可
file1, err := c.FormFile("face1")
file2, err := c.FormFile("face2")

多文件上传相同名

html

<form action="/filesupload" method="post" enctype="multipart/form-data">
	<!-- name  命名 建议带上[]  便区分是同名上传上传-->
    选择文件1: <input type="file" name="face[]">
    选择文件2: <input type="file" name="face[]">
    <input type="submit" value="提交">
</form>

go

	//多文件上传
	r.POST("/filesupload", func(c *gin.Context) {
		form, _ := c.MultipartForm()
		files := form.File["face[]"]
		
		//遍历保存文件
		for _, file := range files {
			dst := path.Join("./static/upload", file.Filename)
			c.SaveUploadedFile(file, dst)
		}

		c.String(200, "success uploadFiles")

	})

按照日期保存文件

将 上传的文件自动保存到 以当前日期命名的文件夹内

go 工具类

import "time"

// 获取时间戳
func GetUnix() int64 {
	return time.Now().Unix()
}

// 获取当前日期
func GetNowDate() string {
	date := "2006-01-01 15:04:05"
	return time.Now().Format(date)
}

// 获取年月日
func GetDay() string {
	template := "20060102"
	return time.Now().Format(template)

}

逻辑代码


	//按照日期保存文件
	r.POST("/fileupload3", func(c *gin.Context) {
		//1.获取上传的文件
		file, err := c.FormFile("face")
		if err == nil {
			//2.创建当前日期的文件目录
			// 获取当前 日期
			day := models.GetDay()
			dir := "./static/upload/" + day
			err := os.MkdirAll(dir, 0666)
			if err != nil {
				fmt.Println(err)
				c.String(200, "创建文件失败")
				return
			}
			// 3.对文件使用时间戳格式命名
			// 获取当前时间戳
			date := models.GetUnix()
			//注意文件名不能有空格, 否则不能正常保存
			filename := strconv.FormatInt(date, 10) + path.Ext(file.Filename)
			dst := path.Join(dir, filename)
			fmt.Println(dst)
			c.SaveUploadedFile(file, dst)
			c.String(200, "success")
		} else {
			c.String(200, "error")
		}
	})

gin中的Cookie

设置cookie 字段解析

SameSite详细解析

	http.SetCookie(c.Writer, &http.Cookie{
		Name:     name,  
		Value:    url.QueryEscape(value),  
		MaxAge:   maxAge,  // 最大存活时间,单位秒小于0 的时候浏览器将会删除该cookie
		Path:     path,  //  路径
		Domain:   domain,   // 域名
		SameSite: c.sameSite, // 限制第三方 Cookie,从而减少安全风险。
		Secure:   secure,     //  是否只能在https 下操作
		HttpOnly: httpOnly,   //  是否允许前端访问
	}

cookie 使用

//设置cookie
c.SetCookie("username", "张三", 120, "/", "localhost", false, true)
//获取cookie		
c.Cookie("username")

多个二级域名共享cookie

假设域名: ddac.com

二级域名 a.ddac.com

setcookie域名: a.ddac.com

c.SetCookie("username", "张三", 120, "a.ddac.com", ".", false, true)

多个 域名共享 cookie

setcookie域名: .ddac.com

[xxx].ddac.com 类型的域名都能够共享cookie

c.SetCookie("username", "张三", 120, "/", ".ddac.com", false, true)

gin中的session 中间件

session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而session保存在服务器上。

session工作流程

当客户端浏览器第–次访问服务器并发送请求时,服务器端会创建一一个session对象,生成一个类似于key,value 的键值对,然后将value保存到服务器将key(cookie)返回到浏览器(客户)端。浏览器下次访问时会携带key(cookie),找到对应的session(value)。

安装

go get github.com/gin-contrib/sessions

**官方地址: **

//配置session 中间件
	//创建基于cookie 的存储引擎, secret1111: 适用于加密的秘钥
	//store := cookie.NewStore([]byte("secret1111"))
	store, _ := redis.NewStore(10, "tcp", "43.139.17.97:13654", "45928ddacQWE", []byte("secret1111"))
	//store 存储引擎
	r.Use(sessions.Sessions("hello", store))

	//将数据保存到 redis

	//设置/获取sessions
	r.GET("/hello", func(c *gin.Context) {
		//设置session
		session := sessions.Default(c)
		//设置session过期时间
		session.Options(sessions.Options{
			MaxAge: 3600*6, // 单位秒
		})
		if session.Get("hello") != "world" {
			session.Set("hello", "redis")
			//注意需要保存
			session.Save()
		}

		c.JSON(200, gin.H{"hello": session.Get("hello")})
	})

session 简单使用

//配置session 中间件
	//创建基于cookie 的存储引擎, secret1111: 适用于加密的秘钥
	store := cookie.NewStore([]byte("secret1111"))
	//store 存储引擎
	r.Use(sessions.Sessions("hello", store))

	//将数据保存到 redis

	//设置/获取sessions
	r.GET("/hello", func(c *gin.Context) {
		//设置session
		session := sessions.Default(c)
		//设置session过期时间
		session.Options(sessions.Options{
			MaxAge: 3600 * 6, // 单位秒
		})
		if session.Get("hello") != "world" {
			session.Set("hello", "redis")
			//注意需要保存
			session.Save()
		}

		c.JSON(200, gin.H{"hello": session.Get("hello")})
	})

将session 保存到redis中

将 store := cookie.NewStore([]byte(“secret1111”)) 替换为

	store, _ := redis.NewStore(10, "tcp", "43.139.17.97:13654", "45928ddacQWE", []byte("secret1111"))

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

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

相关文章

Revit标注时尺寸界线的设置及标注避让调整功能

一、Revit标注时尺寸界线的设置 利用墙体一次性标注轴网尺寸时常遇到如图1所示尺寸边界线上翻的情况&#xff0c;需要手动将其逐一调整&#xff0c;十分繁琐&#xff0c;有没有更为快速地解决方法呢&#xff1f; 此问题可以通过为这些尺寸界线反向显示的尺寸标注单独设置一个新…

CHK文件丢失怎么办?chk文件恢复技巧就看这一个!

很多人不知道CHK文件是什么&#xff0c;其实它是一种后缀名为CHK格式的文件&#xff0c;也属于日常生活中比较常见的文件格式。通常&#xff0c;当用户使用“磁盘碎片整理”时&#xff0c;电脑系统会生成一串“群集”文件&#xff0c;这些文件主要用于保存计算机删除的文件的一…

【中等】最长回文子串-C语言实现

题目链接&#xff1a;https://leetcode.cn/problems/longest-palindromic-substring/思路&#xff1a;起始位置为初始位置向右走&#xff0c;即第一次a为起始位置&#xff0c;第二次b为起始位置结束位置从末尾向左走&#xff0c;即第一次c为末尾&#xff0c;第二次b为末尾&…

BMS中常用的NTC温敏电阻及代码实现

1、什么是NTC&#xff1f; NTC热敏电阻是一种负温度系数的热敏电阻&#xff0c;它的性性是阻值随温度的升高而降低&#xff0c;主要作用是对温度的测量及补偿&#xff0c;也用于NTC温度传感器的制作&#xff0c;常用的使用范围在-55℃至200℃之间。 2、NTC的主要技术参数有哪些…

重发布-路由策略实验1(1.8)

目标&#xff1a; 1、首先为每个路由器配置环回和每个接口的ip r1&#xff1a; [r1]interface lo0 [r1-LoopBack0]ip add 1.1.1.1 24 [r1-LoopBack0]int gi 0/0/0 [r1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 [r1-GigabitEthernet0/0/0]int gi 0/0/1 [r1-GigabitEthernet0/…

Linux--多线程(2)

目录1. 条件变量2. 生产者消费者模型2.1 概念3. 基于BlockingQueue的生产者消费者模型3.1 概念3.2 等待函数3.3 等待函数的功能3.4 唤醒函数4. 模型复盘5. 总代码1. 条件变量 当一个线程互斥地访问某个变量或者临界资源时&#xff0c;它可能发现在其它线程改变状态之前&#x…

物以类聚人以群分,通过GensimLda文本聚类构建人工智能个性化推荐系统(Python3.10)

众所周知&#xff0c;个性化推荐系统能够根据用户的兴趣、偏好等信息向用户推荐相关内容&#xff0c;使得用户更感兴趣&#xff0c;从而提升用户体验&#xff0c;提高用户粘度&#xff0c;之前我们曾经使用协同过滤算法构建过个性化推荐系统&#xff0c;但基于显式反馈的算法就…

【Java寒假打卡】Java基础-集合Map

【Java寒假打卡】Java基础-集合Map基本使用Map集合的基本功能Map集合的第一种遍历方式Map集合的第二种遍历方式案例&#xff1a;HashMap集合存储ArrayList元素并遍历案例&#xff1a;统计字符串中每一个字符出现的次数Collections操纵集合基本使用 创建Map集合的对象&#xff1…

金融历史数据导入之股票 level2 逐笔篇

在部署完 DolphinDB 后&#xff0c;将历史数据导入数据库是后续进行数据查询、计算和分析的基础。为协助用户快速导入数据&#xff0c;本文档基于 DolphinDB 已有的教程与大量用户的实践经验&#xff0c;从操作者角度出发&#xff0c;以 CSV 格式的文件为例&#xff0c;详细介绍…

通讯电平转换电路中的经典设计

今天给大家分享几个通讯电平转换电路。 有初学者问&#xff1a;什么是电平转换&#xff1f;举个例子&#xff0c;比如下面这个电路&#xff1a; 单片机的工作电压是5V&#xff0c;蓝牙模块的工作电压是3.3V&#xff0c;两者之间要进行通讯&#xff0c;TXD和RXD引脚就要进行连接…

Revit里轴网隐藏尺寸标注跟着消失?快速轴网距离标注

一、Revit中链接项目文件轴网的巧妙处理 问题&#xff1a;在单元式住宅体系中&#xff0c;轴网的使用主要是对尺寸标注的影响&#xff0c;如果要将子文件链接到父文件中&#xff0c;需要隐藏轴网&#xff0c;这样与轴网关联的尺寸标注就会消失。 关于尺寸标注与轴网隐藏方式的关…

Java IO流(基础详解,快速上手!)

文章目录概述什么是IO流&#xff1f;常用的文件操作获取文件操作目录操作IO流的原理和分类概述 在Java的学习中&#xff0c;文件和IO流是一个十分重要的板块。在Java中&#xff0c;File是文件和目录路径名的抽象表示。文件和目录可以通过File封装成对象。对File而言&#xff0…

前端 base64与图片相互转换

base64转图片 如下图&#xff1a;&#xff08;后端返回的数据&#xff09; <img :src"baseImg" >let baseImg "" this.baseImg "data:image/png;base64," data?.flowCharbase64转换图片文件 base64ImgtoFile (dataurl, filename …

QT 学习笔记(十六)

文章目录一、TCP 传文件流程图1. 服务器端流程2. 客户端流程二、TCP 传文件操作实现1. 服务器端2. 客户端3. TCP 传文件实现现象三、服务器端和客户端实现代码1. 主函数 main.c2. 服务器端头文件 serverwidget.h3. 服务器端源文件 serverwidget.cpp4. 客户端头文件 clientwidge…

某医院的实战渗透测试(组合拳)

实战渗透一、前言二、Spring信息泄露三、Redis写公钥四、文章来源一、前言 项目是内网环境下进行&#xff0c;所以通过vpn接入内网之后进行目标系统的测试。&#xff08;信息泄露redis写公钥&#xff09; 二、Spring信息泄露 访问客户给的目标地址通过代理把流量转给了BurpS…

零基础学软件测试有前途吗?

随着软件工程活动的不断演化&#xff0c;测试工作某种程度上是可以很大幅度提高软件的产品质量以及提升用户的使用满意度&#xff0c;因此软件测试工程师的地位在企业中也越来越受到重视。不少零基础学IT的朋友也开始把软件测试作为一个绝佳的选择对象&#xff0c;那么零基础学…

leetcode.1806 还原排列的最少操作步数 - 模拟 + lcm

​​​​​​1806. 还原排列的最少操作步数 本题是数论题 共介绍4种解题方法 目录 1、所有置换环长度的最小公倍数 2、最小操作数是最大环长度 3、1或n-2所在环长度即为最大置换环长度 4、暴力模拟 思路&#xff1a; 因为数据范围很小 所以可以直接模拟 也可以优化一下—…

Python 模型训练:LSTM 时间序列销售额预测(训练、保存、调用)

LSTM (long short-term memory) 长短期记忆网络&#xff0c;具体理论的就不一一叙述&#xff0c;直接开始 流程一、数据导入二、数据归一化三、划分训练集、测试集四、划分标签和属性五、转换成 LSTM 输入格式六、设计 LSTM 模型6.1 直接建模6.2 找最好七、测试与图形化展示八、…

JavaSE-07

字节流输入输出数据&#xff1a; InputStream和OutputStream作为字节流输入输出流的超类。 字节流写数据时千万记得close关闭资源&#xff0c;可设置追加写为true 字节流读数据时&#xff0c;FileInputStream a new FileInputStream (“”); int by a.read(); char b (char…

隐蔽信道学习

隐蔽信道作为一种能够在不被系统感知的情况下稳定窃取秘密信息的通信手段&#xff0c;尽管其带宽通常较低&#xff0c;但其设计上的复杂性和多样性&#xff0c;使得常规的流量审计系统难以对抗或检测。同时&#xff0c;隐蔽信道也是密钥、身份认证、商业机密等秘密信息传输的重…