Go语言使用net/http实现简单登录验证和文件上传功能

     最近再看Go语言web编程,go语言搭建Web服务器,既可以用go原生的net/http包,也可以用gin/fasthttp/fiber等这些Web框架。本博客使用net/http模块编写了一个简单的登录验证和文件上传的功能,在此做个简单记录。

目录

1.文件目录结构

2.编译运行

3.用户登录

 4.文件上传

5.mime/multipart模拟form表单上传文件


代码如下:

package mainimport ("fmt""html/template""io""log""net/http""os"
)/*
go运行方式:
(1)解释运行
go run main.go(2)编译运行
--使用默认名
go build main.go
./main
--指定可执行程序名
go build -o test main.go
./test
*/// http://127.0.0.1:8181/login
func login(w http.ResponseWriter, r *http.Request) {fmt.Println("method", r.Method)if r.Method == "GET" {t, _ := template.ParseFiles("login.html")t.Execute(w, nil)/*//字符串拼装表单html := `<html><head><title>上传文件</title></head><body><form enctype="multipart/form-data" action="http://localhost:8181/upload" method="post"><input type="file" name="uploadfile" /><input type="hidden" name="token" value="{{.}}" /><input type="submit" value="upload" /></form></body></html>`crutime := time.Now().Unix()h := md5.New()io.WriteString(h, strconv.FormatInt(crutime, 10))token := fmt.Sprintf("%x", h.Sum(nil))t := template.Must(template.New("test").Parse(html))t.Execute(w, token)*/} else {r.ParseForm()fmt.Println("username", r.Form["username"])fmt.Println("password", r.Form["password"])fmt.Fprintf(w, "登录成功")}
}// http://127.0.0.1:8181/upload
func upload(writer http.ResponseWriter, r *http.Request) {//表示maxMemory,调用ParseMultipart后,上传的文件存储在maxMemory大小的内存中,//如果大小超过maxMemory,剩下部分存储在系统的临时文件中r.ParseMultipartForm(32 << 10)//根据input中的name="uploadfile"来获得上传的文件句柄file, header, err := r.FormFile("uploadfile")if err != nil {fmt.Fprintf(writer, "上传出错")fmt.Println(err)return}defer file.Close()/*fmt.Printf("Uploaded File: %+v\n", header.Filename)fmt.Printf("File Size: %+v\n", header.Size)// 注意此处的header.Header是textproto.MIMEHeader类型 ( map[string][]string )fmt.Printf("MIME Type: %+v\n", header.Header.Get("Content-Type"))// 将文件保存到服务器指定的目录(* 用来随机数的占位符)tempFile, err := ioutil.TempFile("uploads", "*"+header.Filename)*/fmt.Println("handler.Filename", header.Filename)f, err := os.OpenFile("./filedir/"+header.Filename, os.O_WRONLY|os.O_CREATE, 0666)if err != nil {fmt.Println(err)fmt.Fprintf(writer, "上传出错")return}defer f.Close()io.Copy(f, file)fmt.Fprintf(writer, "上传成功")
}func common_handle() {http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello world !"))})http.HandleFunc("/login", login)http.HandleFunc("/upload", upload)
}func main1() {common_handle()//监听8181端口err := http.ListenAndServe(":8181", nil)if err != nil {log.Fatal("err:", err)}
}// 声明helloHandler
type helloHandler struct{}// 定义helloHandler
func (m11111 *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello world, this is my first golang programe !"))
}func welcome(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Welcome to golang family !"))
}func main2() {a := helloHandler{}//使用http.Handlehttp.Handle("/hello", &a)http.Handle("/welcome", http.HandlerFunc(welcome))common_handle()server := http.Server{Addr:    "127.0.0.1:8181",Handler: nil, // 对应DefaultServeMux路由}server.ListenAndServe()
}func main() {main2()
}

login.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"/><title>欢迎进入首页</title></head><body><h3>登录测试</h3><hr/><form action="http://localhost:8181/login" method="post"><table border=0 title="测试"><tr><td>用户名:</td><td><input type="text" name="username"></td></tr><tr><td>密码:</td><td><input type="password" name="password"></td></tr><tr><td colspan=2><input type="reset" /><input type="submit" value="登录" /></td></tr></table></form><br><h3>文件上传测试</h3><hr/><form action="http://localhost:8181/upload" method="post" enctype="multipart/form-data"><input type="file" name="uploadfile"/><input type="submit" value="upload"></form></body>
</html>

1.文件目录结构

2.编译运行

 

3.用户登录

http://127.0.0.1:8181/login

 4.文件上传

 

5.mime/multipart模拟form表单上传文件

       使用mime/multipart包,可以将multipart/form-data数据解析为一组文件和表单字段,或者使用multipart.Writer将文件和表单字段写入HTTP请求体中。

       以下例子中首先打开要上传的文件,然后创建一个multipart.Writer,用于构造multipart/form-data格式的请求体。我们使用CreateFormFile方法创建一个multipart.Part,用于表示文件字段,将文件内容复制到该Part中。我们还使用WriteField方法添加其他表单字段。然后,我们关闭multipart.Writer,以便写入Content-Type和boundary,并使用NewRequest方法创建一个HTTP请求。我们将Content-Type设置为multipart/form-data,并使用默认的HTTP客户端发送请求。最后,我们读取并处理响应。

关于什么是multipart/form-data?
multipart/form-data的基础是post请求,即基于post请求来实现的
multipart/form-data形式的post与普通post请求的不同之处体现在请求头,请求体2个部分
1)请求头:
必须包含Content-Type信息,且其值也必须规定为multipart/form-data,同时还需要规定一个内容分割符用于分割请求体中不同参数的内容(普通post请求的参数分割符默认为&,参数与参数值的分隔符为=)。
具体的头信息格式如下:
Content-Type: multipart/form-data; boundary=${bound}
其中${bound} 是一个占位符,代表我们规定的具体分割符;可以自己任意规定,但为了避免和正常文本重复了,尽量要使用复杂一点的内容。如:—0016e68ee29c5d515f04cedf6733
比如有一个body为:
--0016e68ee29c5d515f04cedf6733\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=text\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nwords words words wor=\r\nds words words =\r\nwords words wor=\r\nds words words =\r\nwords words\r\n--0016e68ee29c5d515f04cedf6733\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=submit\r\n\r\nSubmit\r\n--0016e68ee29c5d515f04cedf6733--

2)请求体:
它也是一个字符串,不过和普通post请求体不同的是它的构造方式。普通post请求体是简单的键值对连接,格式如下:
k1=v1&k2=v2&k3=v3
而multipart/form-data则是添加了分隔符、参数描述信息等内容的构造体。
具体格式如下:
--${bound}
Content-Disposition: form-data; name="Filename" //第一个参数,相当于k1;然后回车;然后是参数的值,即v1
HTTP.pdf //参数值v1
--${bound} //其实${bound}就相当于上面普通post请求体中的&的作用
Content-Disposition: form-data; name="file000"; filename="HTTP协议详解.pdf" //这里说明传入的是文件,下面是文件提
Content-Type: application/octet-stream //传入文件类型,如果传入的是.jpg,则这里会是image/jpeg %PDF-1.5
file content
%%EOF
--${bound}
Content-Disposition: form-data; name="Upload"
Submit Query
--${bound}--
都是以${bound}为开头的,并且最后一个${bound}后面要加—

test.go

package mainimport ("bytes""fmt""io""io/ioutil""mime/multipart""net/http""os""path/filepath"
)func main() {// 需要上传的文件路径filePath := "image_2023_06_29T11_46_39_023Z.png"// 打开要上传的文件file, err := os.Open(filePath)if err != nil {fmt.Println("Failed to open file:", err)return}defer file.Close()// 创建multipart.Writer,用于构造multipart/form-data格式的请求体var requestBody bytes.BuffermultipartWriter := multipart.NewWriter(&requestBody)// 创建一个multipart.Part,用于表示文件字段part, err := multipartWriter.CreateFormFile("uploadfile", filepath.Base(filePath))if err != nil {fmt.Println("Failed to create form file:", err)return}// 将文件内容复制到multipart.Part中_, err = io.Copy(part, file)if err != nil {fmt.Println("Failed to copy file content:", err)return}// 添加其他表单字段multipartWriter.WriteField("title", "My file")// 关闭multipart.Writer,以便写入Content-Type和boundaryerr = multipartWriter.Close()if err != nil {fmt.Println("Failed to close multipart writer:", err)return}// 创建HTTP请求req, err := http.NewRequest("POST", "http://127.0.0.1:8181/upload", &requestBody)if err != nil {fmt.Println("Failed to create request:", err)return}// 设置Content-Type为multipart/form-datareq.Header.Set("Content-Type", multipartWriter.FormDataContentType())// 发送HTTP请求client := http.DefaultClientresp, err := client.Do(req)if err != nil {fmt.Println("Failed to send request:", err)return}defer resp.Body.Close()// 处理响应respBody, err := ioutil.ReadAll(resp.Body)if err != nil {fmt.Println("Failed to read response:", err)return}fmt.Println("Response:", string(respBody))
}

编译执行:

go build test.go 

./test 

运行结果展示:

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

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

相关文章

Spring bean 生命周期

在互联网领域中&#xff0c;Spring框架扮演着重要的角色。作为一个开源的Java应用程序开发框架&#xff0c;Spring提供了一种灵活而强大的方式来构建可扩展的应用程序。Spring框架中的一个重要概念是Bean&#xff0c;它是Spring应用程序的基本构建块之一。了解Spring Bean的生命…

多元分类预测 | Matlab粒子群算法(PSO)优化混合核极限学习机(HKELM)分类预测,多特征输入模型,PSO-HKELM分类预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元分类预测 | Matlab粒子群算法(PSO)优化混合核极限学习机(HKELM)分类预测,多特征输入模型,PSO-HKELM分类预测 多特征输入单输出的二分类及多分类模型。程序内注释详细,直接替换数据就可以用。程序语言为matl…

Unity UGUI Canvas Overlay模式获取屏幕坐标

UGUI Canvas Overlay模式获取屏幕坐标 &#x1f354;效果&#x1f371;获取 &#x1f354;效果 &#x1f371;获取 ui的position就是屏幕坐标(●’◡’●) var screenPos new Vector2(transform.position.x, transform.position.y);

力扣 112. 路径总和

题目来源&#xff1a;https://leetcode.cn/problems/path-sum/description/ C题解1&#xff1a;递归法&#xff0c;前序遍历。先把当前节点加入sumtmp&#xff0c;再判断是不是叶子节点&#xff0c;如果是则判断sumtmp与targetSum的大小&#xff0c;不是则对其左右子树再次进行…

React解决setState异步带来的多次修改合一和修改后立即使用没有变化问题

我们编写这样一段代码 import React from "react" export default class App extends React.Component {constructor(props){super(props);this.state {cont: 0}}componentDidMount() {this.setState({cont: this.state.cont1})}render(){return (<div>{ thi…

【前端|CSS系列第2篇】CSS零基础入门之常用样式属性

欢迎来到CSS零基础入门系列的第二篇博客&#xff01;作为前端开发的关键技术之一&#xff0c;CSS&#xff08;层叠样式表&#xff09;能够为网页添加各种样式和布局效果。对于前端零基础的小白来说&#xff0c;了解和掌握CSS的常用样式属性是入门的关键。本篇博客将带你深入了解…

全志科技T507-H工业核心板规格书(4核ARM Cortex-A53,主频1.416GHz)

1 核心板简介 创龙科技SOM-TLT507是一款基于全志科技T507-H处理器设计的4核ARM Cortex-A53全国产工业核心板&#xff0c;主频高达1.416GHz。核心板CPU、ROM、RAM、电源、晶振等所有元器件均采用国产工业级方案&#xff0c;国产化率100%。 核心板通过邮票孔连接方式引出MIPI CS…

时间序列分解 | Matlab 互补集合经验模态分解(CEEMD)的信号分解

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 时间序列分解 | Matlab 互补集合经验模态分解(CEEMD)的信号分解 部分源码 %---------------------

TI AM64x工业核心板硬件说明书(双核ARM Cortex-A53 + 单/四核Cortex-R5F + 单核Cortex-M4F,主频1GHz)

1 硬件资源 创龙科技SOM-TL64x是一款基于TI Sitara系列AM64x双核ARM Cortex-A53 单/四核Cortex-R5F 单核Cortex-M4F设计的多核工业级核心板&#xff0c;通过工业级B2B连接器引出5x TSN Ethernet、9x UART、2x CAN-FD、GPMC、PCIe/USB 3.1等接口。核心板经过专业的P…

记录好项目D20

记录好项目 你好呀&#xff0c;这里是我专门记录一下从某些地方收集起来的项目&#xff0c;对项目修改&#xff0c;进行添砖加瓦&#xff0c;变成自己的闪亮项目。修修补补也可以成为毕设哦 本次的项目是个springbootvue的图书管理系统项目 一、系统介绍 本项目前后端分离 …

代码源 线段树模板

线段树1 思路&#xff1a; 我们需要维护的东西是序列的最小值和最小值个数 这道题没有修改操作&#xff0c;因此不考虑修改 然后考虑Pushup 最小值很简单&#xff0c;直接取min 最小值个数怎么维护呢&#xff1f;考虑这个区间需要维护的值如何从左右两个区间获得 如果左右…

Scrpay框架之MongoDB--Index

目录 MongoDB-Index 概念 索引类型 创建索引 注意 样例 索引的查看 删除索引 语法 样例 检测索引的速度优势 Mongo-Unique Index And Compound Index 唯一索引&#xff08;Unique Index&#xff09; 添加唯一索引的语法 利用唯一索引进行数据去重 复合索引&a…