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

news2024/11/26 0:24:52

     最近再看Go语言web编程,使用net/http模块编写了一个简单的登录验证和文件上传的功能,在此做个简单记录。

目录

1.文件目录结构

2.编译运行

3.用户登录

 4.文件上传

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


代码如下:

package main

import (
	"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.Handle
	http.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 main

import (
	"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.Buffer
	multipartWriter := 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和boundary
	err = 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-data
	req.Header.Set("Content-Type", multipartWriter.FormDataContentType())

	// 发送HTTP请求
	client := http.DefaultClient
	resp, 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.coloradmin.cn/o/701375.html

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

相关文章

【C语言】递归实战,通过几个例子带你深入走进递归算法

君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 Hello&#xff0c;这里是君兮_&#xff0c;今天给大家带来一篇递归的实战教学文章&#xff0c;由于递归算法不仅对于初学者十分不易理解并且在我们以后的数据结构中也非常重要。我们今天就通过几个应用递归的实际例子来给…

Apache Doris 在头部票务平台的应用实践:报表开发提速数十倍、毫秒级查询响应

作者&#xff5c;国内某头部票务平台 大数据开发工程师 刘振伟 本文导读&#xff1a; 随着在线平台的发展&#xff0c;票务行业逐渐实现了数字化经营&#xff0c;企业可以通过在线销售、数字营销和数据分析等方式提升运营效率与用户体验。基于此&#xff0c;国内某头部票务平…

【Java】Java核心 81:Git 教程(4)差异比较 版本回退

文章目录 06.GIT本地操作-差异比较目标内容小结 07.GIT本地操作-版本回退目标内容小结 在Git中&#xff0c;可以使用差异比较命令和版本回退命令来查看文件之间的差异并回退到早期的版本。 以下是对这些操作的简要解释&#xff1a; 差异比较&#xff1a;你可以使用git diff命…

本地Linux 部署 Dashy 并远程访问

文章目录 简介1. 安装Dashy2. 安装cpolar3.配置公网访问地址4. 固定域名访问 转载自cpolar极点云文章&#xff1a;本地Linux 部署 Dashy 并远程访问 简介 Dashy 是一个开源的自托管的导航页配置服务&#xff0c;具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你…

video-04-videojs配置及使用

videojs是一种轻框架&#xff0c;可以帮我们快速开发一个video视频组件 目录 一、参考资料 二、引入videojs 三、简单了解使用 四、配置项和事件 4.1 常用配置项 4.2 常用事件 4.3 常用方法 4.4 网络状态 4.5 播放状态 4.6 视频控制 五、实例&#xff08;可直接复制…

升级iOS 17测试版后如何降级?iOS17降级教程

对于已经升级到 iOS 17 测试版的用户&#xff0c;如果在体验过程中&#xff0c;感觉到并不是那么稳定&#xff0c;例如出现应用程序不适配、电池续航下降、功能无法正常启用等问题&#xff0c;想要进行降级操作&#xff0c;可以参考本教程。 降级前注意事项&#xff1a; 1.由于…

Android 自定义手写签字板,签署姓名,签名

各位大佬好又来记笔记了~ 今天要做的是签字板&#xff0c;实现客户签名功能&#xff0c;直接看效果&#xff1a; 逐个进行签字&#xff0c;可以避免连笔导致识别不清问题。就是想要客户一个一个写&#xff0c;认真写~~。 下面方框显示的“王某才” 其实是三张图片&#xff0c;…

【算法题】动态规划中级阶段之不同路径、最小路径和

动态规划中级阶段 前言一、不同路径1.1、思路1.2、代码实现 二、不同路径 II2.1、思路2.2、代码实现 三、最小路径和3.1、思路3.3、代码实现 总结 前言 动态规划&#xff08;Dynamic Programming&#xff0c;简称 DP&#xff09;是一种解决多阶段决策过程最优化问题的方法。它…

卸载及安装docker的教程-ubuntu

一、前言 万地高楼平地起~ 二、环境 OS&#xff1a;Ubuntu 20.04 64 bit 显卡&#xff1a;NVidia GTX 2080 Ti CUDA&#xff1a;11.2 三、卸载docker 1、删除docker及安装时自动安装的所有包 apt-get autoremove docker docker-ce docker-engine docker-ce-*for pkg in …

linux -信号量semphore分析

linux -信号量分析 1 struct semaphore和sema_init1.1 struct semaphore1.2 sema_init 2 down3 up4 down_interruptible5 down_killable6 down_timeout7 down_trylock 基于linux-5.15分析&#xff0c;信号量在使用是是基于spin lock封装实现的。 1 struct semaphore和sema_ini…

爬虫入门指南:如何使用正则表达式进行数据提取和处理

文章目录 正则表达式正则表达式中常用的元字符和特殊序列案例 使用正则表达式提取数据案例存储数据到文件或数据库使用SQLite数据库存储数据的示例代码SQLite基本语法创建表格&#xff1a;插入数据&#xff1a;查询数据&#xff1a;更新数据&#xff1a;删除数据&#xff1a;条…

【雕爷学编程】Arduino动手做(137)---MT8870语音解码

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

【uview calendar日历 】如何选择今天之前的数据

在日常工作中&#xff0c;使用uniappuview的ui组件&#xff0c;使用日历组件默认是无法选择当前之前的日期&#xff0c;现在讲下解决的方法 设置 最小的可选日期minDate&#xff0c;最大可选日期maxDate&#xff0c; 默认选中的日期&#xff0c;mode为multiple或range是必须为数…

自定义选项卡组件,选项可插槽html

文件夹xxtabs 四个文件 index暴露 render vue添加虚拟节点到插槽&#xff08;自定义标签结构&#xff09; tabs选项卡整体 abpaneq切换区 tabs.vue <template><div class"gnip-tab"><div class"gnip-tab-nav"><divv-for"(item,…

“sudo”组不存在”或“用户不在 sudoers 文件中。此事将被报告”

解决方法: 使用命令&#xff1a;usermod -a -G sudo tom (换成其他的用户名&#xff0c;也是一个道理)&#xff0c;不过还是不行。 实际解决还是要执行 sudo visudo &#xff0c;在这个文件中去添加用户 这样修改之后&#xff0c;保存并退出&#xff0c;亲测有效&#xff01; …

【FFmpeg实战】AAC编码介绍

AAC&#xff08;Advanced Audio Coding&#xff0c;译为&#xff1a;高级音频编码&#xff09;&#xff0c;是由Fraunhofer IIS、杜比实验室、AT&T、Sony、Nokia等公司共同开发的有损音频编码和文件格式。 对比MP3 AAC被设计为MP3格式的后继产品&#xff0c;通常在相同的比…

训练自己的ChatGPT 语言模型(一).md

0x00 Background 为什么研究这个&#xff1f; ChatGPT在国内外都受到了广泛关注&#xff0c;很多高校、研究机构和企业都计划推出类似的模型。然而&#xff0c;ChatGPT并没有开源&#xff0c;且复现难度非常大&#xff0c;即使到现在&#xff0c;没有任何单位或企业能够完全复…

Atlas200 DK A2与Arduino进行UART串口通信

我们在做一些人工智能的应用开发时往往使用人工智能开发板作为上位机&#xff08;比如我们的小滕&#xff09;&#xff0c;Arduino、stm32等作为下位机控制板&#xff0c;通过上位机进行人工智能模型的推理之后进而给下位机传输对应的控制命令实现智能控制。那么如何实现两者的…

简化交互体验——探索Gradio的ClearButton模块

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

ndarray对象怎样创建?ndarray基本属性列举

numpy中包含一个N维数组对象&#xff0c;即ndarray对象&#xff0c;该对象具有矢量算术能力和复杂的广播能力&#xff0c;常用于科学计算。ndarray对象中的元素可以通过索引访问&#xff0c;索引序号从0开始;ndarray对象中存储的所有元素的类型必须相同。创建ndarray对象的方式…