Gin框架(3)

文件上传与下载

文件上传

单文件上传

单文件上传指的是一次只上传一个文件。在Gin中,可以使用c.SaveUploadedFile方法来保存单个上传的文件。

// SaveUploadedFile uploads the form file to specific dst.
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error {src, err := file.Open()if err != nil {return err}defer src.Close()if err=os.MkdirAll(filepath,Dir(dst),0750);err!=nil{return err}out, err := os.Create(dst)if err != nil {return err}defer out.Close()_, err = io.Copy(out, src)return err
}
func singleFileUpload(c *gin.Context) {
// 从表单中获取上传的文件
file, err := c.FormFile("file")
if err != nil {
// 处理错误,可能是文件未找到或其他原因
c.JSON(http.StatusBadRequest, gin.H{"error": "File not found in form"})
return
}// 指定保存文件的路径,这里使用了文件的原始文件名
path := "./" + file.Filename// 保存上传的文件到指定路径
if err := c.SaveUploadedFile(file, path); err != nil {// 处理错误c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})return
}// 返回成功响应
c.JSON(http.StatusOK, gin.H{"message": "File uploaded successfully", "file_path": path})
}

多文件上传

// MultipartForm is the parsed multipart form, including file uploads.
//MultipartForm 方法用于解析和处理 multipart/form-data 类型的请求,这通常用于文件上传
func (c *Context) MultipartForm() (*multipart.Form, error) {err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory)return c.Request.MultipartForm, err
}

Gin框架处理多文件上传的实例

func uploadHandler(c *gin.Context){form,err:=c.MultipartForm()if err!=nil{c.JSON(http.StatusBadRequest,gin.H{"error":"Failed to parse multipart form"})return}// 从form中获取文件切片files := form.File["files"] // "files" 是HTML表单中的input[type=file]的name属性值// 遍历所有上传的文件for _, fileHeader := range files {// 打开文件file, err := fileHeader.Open()if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})return}defer file.Close()// 保存文件到服务器filePath := "path/to/save/" + fileHeader.Filenameerr = c.SaveUploadedFile(file, filePath)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})return}// 处理成功后的逻辑c.JSON(http.StatusOK, gin.H{"message": "File uploaded successfully", "file_path": filePath})}
}

文件下载

1. 使用c.File方法

Gin提供了c.File方法,可以直接从HTTP响应中提供文件。这是最简单的方法之一,但它仅适用于服务于静态文件的场景。

func downloadFile(c *gin.Context) {// 指定文件路径filePath := "path/to/your/file.txt"// 检查文件是否存在if _, err := os.Stat(filePath); err != nil {c.JSON(http.StatusNotFound, gin.H{"error": "File not found"})return}// 设置文件名作为Content-Disposition响应头的一部分c.Header("Content-Disposition", "attachment; filename="+filepath.Base(filePath))// 使用c.File方法发送文件c.File(filePath)
}

2. 使用c.Writerio.Copy

如果你需要更多的控制,比如动态生成文件内容或者处理大文件,可以使用c.Writerio.Copy来手动写入文件内容。

func downloadFile(c *gin.Context) {// 指定文件内容fileContent := "This is the file content."// 设置响应头c.Header("Content-Disposition", "attachment; filename=file.txt")c.Header("Content-Type", "text/plain")// 写入文件内容_, err := io.WriteString(c.Writer, fileContent)if err != nil {c.AbortWithStatus(http.StatusInternalServerError)}
}

3. 流式下载大文件

对于大文件,可以使用流式传输来避免一次性加载整个文件到内存中。

func downloadLargeFile(c *gin.Context) {// 指定大文件路径filePath := "path/to/your/largefile.zip"// 打开文件file, err := os.Open(filePath)if err != nil {c.JSON(http.StatusNotFound, gin.H{"error": "File not found"})return}defer file.Close()// 设置响应头c.Header("Content-Disposition", "attachment; filename="+filepath.Base(filePath))c.Header("Content-Type", "application/octet-stream")// 流式传输文件内容_, err = io.Copy(c.Writer, file)if err != nil {c.AbortWithStatus(http.StatusInternalServerError)}
}

4. 使用http.ServeContent

Go标准库中的http.ServeContent函数可以用于提供文件下载。这个方法允许你利用http.ServeContent的缓存控制和其他功能。

func downloadFile(c *gin.Context) {// 指定文件路径filePath := "path/to/your/file.txt"// 检查文件是否存在if _, err := os.Stat(filePath); err != nil {c.JSON(http.StatusNotFound, gin.H{"error": "File not found"})return}// 打开文件file, err := os.Open(filePath)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "Error opening file"})return}defer file.Close()// 获取文件信息fileInfo, _ := file.Stat()// 设置响应头c.Header("Content-Disposition", "attachment; filename="+filepath.Base(filePath))c.Header("Content-Type", "application/octet-stream")// 使用http.ServeContent进行流式传输http.ServeContent(c.Writer, c.Request, fileInfo, int64(fileInfo.Size()), file)
}

前后端分离

  1. 后端:提供文件下载的HTTP路由处理函数,设置正确的响应头,并发送文件内容给前端。

  2. 前端:通过HTTP请求与后端通信,并处理文件下载过程。

后端实现
package mainimport ("io""net/http""os""path/filepath""github.com/gin-gonic/gin"
)func main() {r := gin.Default()// 文件下载路由r.GET("/download/:filename", func(c *gin.Context) {filename := c.Param("filename") // 获取文件名参数filePath := filepath.Join("path/to/files/", filename) // 定义文件的路径// 检查文件是否存在if _, err := os.Stat(filePath); os.IsNotExist(err) {c.JSON(http.StatusNotFound, gin.H{"message": "File not found"})return}// 设置响应头c.Header("Content-Disposition", "attachment; filename="+filepath.Base(filePath))c.Header("Content-Type", "application/octet-stream")// 打开文件并将其写入响应流file, err := os.Open(filePath)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"message": "Error opening file"})return}defer file.Close()_, err = io.Copy(c.Writer, file)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"message": "Error writing file to response"})return}})r.Run(":8080") // 启动Gin服务器
}
前端实现

前端可以使用标准的HTML <a>标签或者JavaScript来发起文件下载请求。以下是两种常见的前端下载方法:

方法1:使用HTML链接

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>File Download Example</title>
</head>
<body><!-- 下载链接 --><a href="/download/myfile.txt" download>Download File</a>
</body>
</html>

在这个HTML示例中,<a>标签的href属性设置为文件下载的URL,download属性指定了要下载的文件名。

方法2:使用JavaScript


<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>File Download Example</title>
</head>
<body><button id="downloadButton">Download File</button><script>document.getElementById('downloadButton').addEventListener('click', function() {fetch('/download/myfile.txt').then(response => {if (response.ok) return response.blob();throw new Error('Network response was not ok.');}).then(blob => {const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.style.display = 'none';a.href = url;a.download = 'myfile.txt';document.body.appendChild(a);a.click();window.URL.revokeObjectURL(url);}).catch(error => {console.error('There has been a problem with your fetch operation:', error);});});</script>
</body>
</html>

在这个JavaScript示例中,当用户点击按钮时,会发起一个GET请求到文件下载的URL。然后,它将响应转换为Blob,并创建一个临时的URL。接着,它创建一个隐藏的<a>标签,设置href为Blob URL,并指定download属性来触发文件下载。

中间件

Middleware

中间件是Gin中的一个强大的功能,它允许开发者在处理请求的流程中插入自定义的逻辑。中间件可以用于日志记录、用户认证、跨域资源共享(CORS)处理等。
在Gin中,中间件可以通过Use方法全局注册,或者在特定路由组中注册。Gin中的中间件必须是一个gin.HandlerFunc类型

HandlerFunc

处理函数是实际处理请求的函数。它们接收一个*gin.Context对象作为参数,并返回一个响应。处理函数可以是任何满足gin.HandlerFunc类型的函数。

package mainimport ("github.com/gin-gonic/gin"
)func main() {r := gin.Default()// 全局中间件r.Use(LoggingMiddleware)// 特定路由组的中间件v1 := r.Group("/v1")v1.Use(AuthenticationMiddleware)v1.GET("/books", GetBooks)r.Run(":8080")
}// LoggingMiddleware 是一个记录日志的中间件
func LoggingMiddleware(c *gin.Context) {c.Writer.Header().Set("Access-Control-Allow-Origin", "*") // 允许跨域请求start := time.Now()c.Next() // 处理请求latency := time.Since(start)log.Printf("Request took %v", latency)
}

多个中间件

r.GET,后面可以跟很多HandlerFunc方法,这些方法其实都可以叫中间件

package mainimport ("fmt""github.com/gin-gonic/gin"
)func m1(c *gin.Context) {fmt.Println("m1 ...in")
}
func m2(c *gin.Context) {fmt.Println("m2 ...in")
}func main() {router := gin.Default()router.GET("/", m1, func(c *gin.Context) {fmt.Println("index ...")c.JSON(200, gin.H{"msg": "响应数据"})}, m2)router.Run(":8080")
}/*
m1  ...in
index ...
m2  ...in
*/

中间件拦截响

c.Abort() 方法用于立即终止当前请求的后续处理。当你调用这个方法时,Gin 会停止执行当前路由链中的后续中间件和处理函数,并直接返回给客户端一个错误响应(通常是 500 内部服务器错误)。c.Abort()拦截,后续的HandlerFunc就不会执行了

package mainimport ("fmt""github.com/gin-gonic/gin"
)func m1(c *gin.Context) {fmt.Println("m1 ...in")c.JSON(200, gin.H{"msg": "第一个中间件拦截了"})c.Abort()
}
func m2(c *gin.Context) {fmt.Println("m2 ...in")
}func main() {router := gin.Default()router.GET("/", m1, func(c *gin.Context) {fmt.Println("index ...")c.JSON(200, gin.H{"msg": "响应数据"})}, m2)router.Run(":8080")
}

中间件放行

c.Next() 方法用于继续执行当前路由链中的下一个中间件或处理函数。如果你的中间件需要在处理请求之前或之后执行某些操作,但不影响后续中间件的执行,那么你应该在适当的时候调用 c.Next()c.Next(),Next前后形成了其他语言中的请求中间件和响应中间件

package mainimport ("fmt""github.com/gin-gonic/gin"
)func m1(c *gin.Context) {fmt.Println("m1 ...in")c.Next()fmt.Println("m1 ...out")
}
func m2(c *gin.Context) {fmt.Println("m2 ...in")c.Next()fmt.Println("m2 ...out")
}func main() {router := gin.Default()router.GET("/", m1, func(c *gin.Context) {fmt.Println("index ...in")c.JSON(200, gin.H{"msg": "响应数据"})c.Next()fmt.Println("index ...out")}, m2)router.Run(":8080")
}/*
m1 ...in
index ...in
m2 ...in   
m2 ...out  
index ...out
m1 ...out
*/

路由

Router

路由是Gin的核心,负责将请求的URL和HTTP方法映射到相应的处理函数上。Gin使用httprouter作为其路由库,这是一个高性能的路由库,支持RESTful API设计。Gin的路由机制非常灵活,允许开发者定义路由和相应的处理函数。路由可以包含动态参数、查询参数、路由组等。

Group

组是Gin中的一个概念,它允许你将一组路由组织在一起,并且可以为这个组添加前缀路径和中间件。这使得路由的组织和管理变得更加灵活。

以下是定义路由的例子:

func main() {r := gin.Default()// 定义GET请求的路由r.GET("/books/:id", GetBookByID)// 定义POST请求的路由r.POST("/books", CreateBook)// 定义PUT请求的路由r.PUT("/books/:id", UpdateBook)// 定义DELETE请求的路由r.DELETE("/books/:id", DeleteBook)r.Run(":8080")
}// GetBookByID 处理GET请求
func GetBookByID(c *gin.Context) {bookID := c.Param("id")// 根据ID获取书籍信息...c.JSON(http.StatusOK, gin.H{"id": bookID, "message": "Book retrieved"})
}// CreateBook 处理POST请求
func CreateBook(c *gin.Context) {var book Bookif err := c.ShouldBindJSON(&book); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}// 创建新书籍...c.JSON(http.StatusOK, gin.H{"data": book})
}// UpdateBook 处理PUT请求
func UpdateBook(c *gin.Context) {bookID := c.Param("id")var book Bookif err := c.ShouldBindJSON(&book); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}// 更新书籍信息...c.JSON(http.StatusOK, gin.H{"data": book})
}// DeleteBook 处理DELETE请求
func DeleteBook(c *gin.Context) {bookID := c.Param("id")// 删除书籍...c.JSON(http.StatusOK, gin.H{"id": bookID, "message": "Book deleted"})
}

在上面的例子中,我们定义了四个不同的HTTP方法(GET, POST, PUT, DELETE)的路由,每个路由都有一个对应的处理函数。:id是一个动态参数,可以在处理函数中通过c.Param(“id”)获取。

日志

Gin框架提供了一个内置的日志中间件,可以帮助开发者记录HTTP请求和响应的详细信息。这对于调试、监控和分析Web应用程序的行为非常有用。Gin的日志中间件可以通过标准库中的log包或其他第三方日志库来实现。

默认的日志记录

Gin框架的默认实例(通过gin.Default()创建)已经包含了一个默认的日志记录器,它使用标准库的log包。这个日志记录器会输出每个请求的基本信息,包括请求方法、路径、状态码和处理请求所花费的时间。

package mainimport ("github.com/gin-gonic/gin"
)func main() {r := gin.Default()r.GET("/ping", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "pong"})})r.Run(":8080")
}

在这个例子中,当你访问/ping路由时,Gin会自动记录请求的日志信息。

自定义日志中间件

如果你需要更详细的日志记录或想要使用不同的日志库,你可以创建自定义的日志中间件。以下是一个使用标准库log包创建的自定义日志中间件的例子:

package mainimport ("log""net/http""time""github.com/gin-gonic/gin"
)func requestLogger(c *gin.Context) {// 访问开始时间startTime := time.Now()// 处理请求c.Next()// 访问结束时间endTime := time.Now()// 访问耗时latency := endTime.Sub(startTime)// 记录日志log.Printf("%s %s %s %d %s\n", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Writer.Status(), latency)
}func main() {r := gin.New()r.Use(requestLogger)r.GET("/ping", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "pong"})})r.Run(":8080")
}

在这个例子中,我们定义了一个requestLogger函数,它会在每个请求处理之前记录客户端IP、请求方法、请求路径、状态码和处理请求的耗时。然后,我们通过r.Use(requestLogger)将这个日志中间件添加到Gin路由器中。

使用第三方日志库

Gin也可以与第三方日志库一起使用,例如zaplogruszerolog等。这些库提供了更丰富的日志记录功能,包括日志级别、结构化日志记录和异步日志记录等。

使用logrus库创建的自定义日志中间件的例子:


package mainimport ("io/ioutil""net/http""time""github.com/gin-gonic/gin""github.com/sirupsen/logrus"
)// 日志中间件
func LoggerMiddleware() gin.HandlerFunc {return func(c *gin.Context) {// 在请求处理之前记录请求开始的时间start := time.Now()c.Next()// 请求结束后记录请求信息log.WithFields(logrus.Fields{"method": c.Request.Method,"path":    c.Request.URL.Path,"ip":      c.ClientIP(),"status":  c.Writer.Status(),"duration": time.Since(start),}).Info("Request completed")}
}func main() {// 初始化logruslog := logrus.New()log.SetFormatter(&logrus.JSONFormatter{})log.SetOutput(ioutil.Discard) // 设置日志输出到控制台,生产环境可以设置为文件或其他输出// 初始化Gin路由器r := gin.Default()// 使用自定义的日志中间件r.Use(LoggerMiddleware())// 定义一个示例路由r.GET("/", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "Hello, World!"})})// 启动Gin服务器r.Run(":8080")
}

我们首先定义了一个LoggerMiddleware函数,它返回一个gin.HandlerFunc。这个中间件在每个请求处理之前记录请求开始的时间,并在请求处理之后记录请求的详细信息,包括HTTP方法、路径、客户端IP、状态码和处理请求的耗时。

我们使用logrusWithFields方法来添加结构化的日志数据,并使用Info方法来记录日志。logrus.JSONFormatter用于格式化日志输出为JSON格式,而SetOutput方法设置了日志的输出目标,这里我们将其设置为控制台(ioutil.Discard

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

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

相关文章

对 NGINX、Kong 和 Amazon 的 API 管理解决方案进行基准测试:它们能否交付实时 API?

原文作者&#xff1a;Alessandro Fael Garcia of F5 原文链接&#xff1a;对 NGINX、Kong 和 Amazon 的 API 管理解决方案进行基准测试&#xff1a;它们能否交付实时 API&#xff1f; 转载来源&#xff1a;NGINX 开源社区 NGINX 唯一中文官方社区 &#xff0c;尽在 nginx.org.c…

HAL STM32 硬件I2C方式读取AS5600磁编码器获取角度例程

HAL STM32 硬件I2C方式读取AS5600磁编码器获取角度例程 &#x1f4cd;相关篇《STM32 软件I2C方式读取AS5600磁编码器获取角度例程》 ✨stm32使用硬件I2C去读取角度数据&#xff0c;通过STM32CubeMX工具配置工程&#xff0c;读取角度数据&#xff0c;只需要调用一个函数&#xf…

常见的服务器技术

常见的服务器技术 1.虚拟化技术&#xff1a;虚拟化技术允许在一台物理服务器上创建多个虚拟服务器&#xff0c;每个虚拟服务器都可以独立运行不同的操作系统和应用程序。这大大提高了服务器的资源利用率&#xff0c;并提供了更好的灵活性、可扩展性和可靠性。 2.负载均衡技术&…

谷歌(Google)技术面试——在线评估问题(一)

谷歌&#xff08;Google&#xff09;面试过程的第一步&#xff0c;你可能会收到一个在线评估链接。 评估有效期为 7 天&#xff0c;包含两个编码问题&#xff0c;需要在一小时内完成。 以下是一些供你练习的在线评估问题。 在本章结尾处&#xff0c;还提供了有关 Google 面试不…

使用 RisingWave、NATS JetStream 和 Superset 进行实时物联网监控

在物联网&#xff08;IoT&#xff09;背景下&#xff0c;处理实时数据会遇到一些特定的障碍&#xff0c;如边缘计算资源不足、网络条件限制、扩展性存在问题、设备间有多样性差异。要克服这些挑战&#xff0c;需要高效的边缘计算技术、强大的安全措施、标准化协议、可扩展的管理…

接口自动化框架搭建(六):多进程执行

1&#xff0c;背景目的 当测试用例太多之后&#xff0c;想缩短执行时间&#xff0c;就需要多线程或者多进程执行。 多线程执行&#xff1a; 每条测试用例是独立的&#xff0c;测试用例之间的参数不能共同使用 采坑举例&#xff1a;接口自动化中请求头是公共参数&#xff0c;…

Sqlite插入单引号和双引号,防止sql注入

1. 方法1 sqlite3_mprintf替换sprintf,%q替换%s. 1.1. 举例 修改前代码 //修改前, hello123写入失败char sql[1000]char* sql sprintf("UPDATE table SET name %s WHERE name_id %d","hello123", 1);rc sqlite3_exec(db, sql, NULL, NULL, &err…

WebGIS 地铁交通线网 | 图扑数字孪生

数字孪生技术在地铁线网的管理和运维中的应用是一个前沿且迅速发展的领域。随着物联网、大数据、云计算以及人工智能技术的发展&#xff0c;地铁线网数字孪生在智能交通和智慧城市建设中的作用日益凸显。 图扑软件基于 HTML5 的 2D、3D 图形渲染引擎&#xff0c;结合 GIS 地图…

人人都离不开的算法:AI 时代的生存指南

文章目录 一、算法在生活中的“无处不在”二、算法在工作学习中的“智慧助力”三、算法在社会发展中的“驱动力量”四、算法带来的“双刃剑”效应五、应对算法挑战的策略《人人都离不开的算法——图解算法应用》编辑推荐1、通俗易懂2、技术科普3、贴近时代、贴近生活4、启发思考…

List、Set、Map 之间的区别是什么?

List、Set和Map之间的主要区别体现在它们的定义、特性、用途和常见实现上。 首先&#xff0c;List、Set和Map都是Java集合框架中的重要接口&#xff0c;用于存储和操作数据&#xff0c;但它们各自有不同的特性。 List&#xff08;列表&#xff09;是一个有序的集合&#xff0…

婴儿沐浴椅CPC认证 亚马逊沐浴椅子CPC认证

婴儿沐浴椅 如果您在亚马逊商城发布商品&#xff0c;则必须遵守适用于这些商品和商品信息的所有联邦、州和地方法律以及亚马逊政策&#xff08;包括本政策&#xff09;。 本政策适用的婴儿沐浴椅 婴儿沐浴椅是一种用于浴缸、盥洗盆或类似沐浴设备中的一种支撑物&#xff0c;…

深入剖析JavaScript中的this(上)

在Javascript中&#xff0c;this 关键字是一个非常重要的概念&#xff0c;this这个关键字可以说是很常见也用的很多&#xff0c;说它简单也很简单&#xff0c;说它难也很难。我们经常会用到this&#xff0c;也经常会因为this头疼&#xff0c;是一个经常被误解和误用的概念&…

基于DWT(离散小波变换)的图像加密水印算法,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

einops中的rearrange的使用方法

einops中的rearrange的使用方法 在 einops 中&#xff0c;rearrange 函数用于对张量进行重排操作&#xff0c;即重新排列张量的维度顺序或形状。它的语法如下&#xff1a; einops.rearrange(tensor, pattern)tensor&#xff1a;要重排的张量。 pattern&#xff1a;用于指定重排…

“继承MonoBehavior的泛型单例“本质上是一个单例模板

"继承MonoBehavior的泛型单例"本质上是一个单例模板&#xff0c;它可以用于管理其他所有继承自MonoBehaviour的单例类。通过继承这个泛型单例模板&#xff0c;可以确保每个单例类只有一个实例&#xff0c;并且这些实例在整个Unity应用程序中都是唯一的。这种模式使得…

华尔街基金经理为什么开始押注MVP币了?

目前&#xff0c;市场非常流行一种新兴的上市策略。 依靠正在被市场认可并有明显增长动力的 Meme 币&#xff0c;并围绕它构建一个社区&#xff0c;继而完成整个生态&#xff0c;最终&#xff0c;将由一系列产品完成生态的繁荣。 通过启动一个与热门 Meme 币原生集成的项目&a…

The Google File System [SOSP‘03] 论文阅读笔记

原论文&#xff1a;The Google File System 1. Introduction 组件故障是常态而非例外 因此&#xff0c;我们需要持续监控、错误检测、容错和自动恢复&#xff01; 按照传统标准&#xff0c;文件数量巨大大多数文件都是通过添加新数据而不是覆盖现有数据来改变的&#xff0c;因…

大数据实验统计-1、Hadoop安装及使用;2、HDFS编程实践;3、HBase编程实践;4、MapReduce编程实践

大数据实验统计 1、Hadoop安装及使用&#xff1b; 一&#xff0e;实验内容 Hadoop安装使用&#xff1a; 1&#xff09;在PC机上以伪分布式模式安装Hadoop&#xff1b; 2&#xff09;访问Web界面查看Hadoop信息。 二&#xff0e;实验目的 1、熟悉Hadoop的安装流程。 2、…

Mybatis plue(二) 核心功能

核心功能 P5 条件构造器 mybatisplus支持各种复杂的where条件&#xff0c;可以满足日常开发的所有需求 wrapper就是条件构造器,wrapper就是顶层的&#xff0c; 示例&#xff1a; 查询出名字带0&#xff0c;存款大于等于1000的人的id,username,info,balance字段 Testvoid te…

简单的安全密码生成器PwGen

什么是 PwGen &#xff1f; PwGen 是一个简单的 Docker Web 应用程序&#xff0c;旨在生成具有可自定义选项的安全密码或密码短语。用户可以选择生成具有特定标准的随机密码或由随机单词组成的密码。其他功能包括在密码中包含大写字母、数字和特殊字符的选项&#xff0c;或者将…