全栈编程

Balance $ 2,317
Item Sold 1230
文章作者: 全栈编程@luboke.com
版权声明: 本文章为go语言体系课视频教程配套电子书,版权归 全栈编程@luboke.com所有,欢迎免费学习,转载必须注明出处!但禁止任何商业用途,否则将受到法律制裁!

Go 语言的 net/http 包提供了一系列用于表示 HTTP 报文的结构,我们可以使用它 处理请求和发送相应,其中 Request 结构代表了客户端发送的请求报文,下面让我们看 一下 Request 结构体

type Request struct {
    // Method指定HTTP方法(GET、POST、PUT等)。对客户端,""代表GET。
    Method string
    // URL在服务端表示被请求的URI,在客户端表示要访问的URL。
    //
    // 在服务端,URL字段是解析请求行的URI(保存在RequestURI字段)得到的,
    // 对大多数请求来说,除了Path和RawQuery之外的字段都是空字符串。
    // (参见RFC 2616, Section 5.1.2)
    //
    // 在客户端,URL的Host字段指定了要连接的服务器,
    // 而Request的Host字段(可选地)指定要发送的HTTP请求的Host头的值。
    URL *url.URL
    // 接收到的请求的协议版本。本包生产的Request总是使用HTTP/1.1
    Proto      string // "HTTP/1.0"
    ProtoMajor int    // 1
    ProtoMinor int    // 0
    // Header字段用来表示HTTP请求的头域。如果头域(多行键值对格式)为:
    //	accept-encoding: gzip, deflate
    //	Accept-Language: en-us
    //	Connection: keep-alive
    // 则:
    //	Header = map[string][]string{
    //		"Accept-Encoding": {"gzip, deflate"},
    //		"Accept-Language": {"en-us"},
    //		"Connection": {"keep-alive"},
    //	}
    // HTTP规定头域的键名(头名)是大小写敏感的,请求的解析器通过规范化头域的键名来实现这点。
    // 在客户端的请求,可能会被自动添加或重写Header中的特定的头,参见Request.Write方法。
    Header Header
    // Body是请求的主体。
    //
    // 在客户端,如果Body是nil表示该请求没有主体买入GET请求。
    // Client的Transport字段会负责调用Body的Close方法。
    //
    // 在服务端,Body字段总是非nil的;但在没有主体时,读取Body会立刻返回EOF。
    // Server会关闭请求的主体,ServeHTTP处理器不需要关闭Body字段。
    Body io.ReadCloser
    // ContentLength记录相关内容的长度。
    // 如果为-1,表示长度未知,如果>=0,表示可以从Body字段读取ContentLength字节数据。
    // 在客户端,如果Body非nil而该字段为0,表示不知道Body的长度。
    ContentLength int64
    // TransferEncoding按从最外到最里的顺序列出传输编码,空切片表示"identity"编码。
    // 本字段一般会被忽略。当发送或接受请求时,会自动添加或移除"chunked"传输编码。
    TransferEncoding []string
    // Close在服务端指定是否在回复请求后关闭连接,在客户端指定是否在发送请求后关闭连接。
    Close bool
    // 在服务端,Host指定URL会在其上寻找资源的主机。
    // 根据RFC 2616,该值可以是Host头的值,或者URL自身提供的主机名。
    // Host的格式可以是"host:port"。
    //
    // 在客户端,请求的Host字段(可选地)用来重写请求的Host头。
    // 如过该字段为"",Request.Write方法会使用URL字段的Host。
    Host string
    // Form是解析好的表单数据,包括URL字段的query参数和POST或PUT的表单数据。
    // 本字段只有在调用ParseForm后才有效。在客户端,会忽略请求中的本字段而使用Body替代。
    Form url.Values
    // PostForm是解析好的POST或PUT的表单数据。
    // 本字段只有在调用ParseForm后才有效。在客户端,会忽略请求中的本字段而使用Body替代。
    PostForm url.Values
    // MultipartForm是解析好的多部件表单,包括上传的文件。
    // 本字段只有在调用ParseMultipartForm后才有效。
    // 在客户端,会忽略请求中的本字段而使用Body替代。
    MultipartForm *multipart.Form
    // Trailer指定了会在请求主体之后发送的额外的头域。
    //
    // 在服务端,Trailer字段必须初始化为只有trailer键,所有键都对应nil值。
    // (客户端会声明哪些trailer会发送)
    // 在处理器从Body读取时,不能使用本字段。
    // 在从Body的读取返回EOF后,Trailer字段会被更新完毕并包含非nil的值。
    // (如果客户端发送了这些键值对),此时才可以访问本字段。
    //
    // 在客户端,Trail必须初始化为一个包含将要发送的键值对的映射。(值可以是nil或其终值)
    // ContentLength字段必须是0或-1,以启用"chunked"传输编码发送请求。
    // 在开始发送请求后,Trailer可以在读取请求主体期间被修改,
    // 一旦请求主体返回EOF,调用者就不可再修改Trailer。
    //
    // 很少有HTTP客户端、服务端或代理支持HTTP trailer。
    Trailer Header
    // RemoteAddr允许HTTP服务器和其他软件记录该请求的来源地址,一般用于日志。
    // 本字段不是ReadRequest函数填写的,也没有定义格式。
    // 本包的HTTP服务器会在调用处理器之前设置RemoteAddr为"IP:port"格式的地址。
    // 客户端会忽略请求中的RemoteAddr字段。
    RemoteAddr string
    // RequestURI是被客户端发送到服务端的请求的请求行中未修改的请求URI
    // (参见RFC 2616, Section 5.1)
    // 一般应使用URI字段,在客户端设置请求的本字段会导致错误。
    RequestURI string
    // TLS字段允许HTTP服务器和其他软件记录接收到该请求的TLS连接的信息
    // 本字段不是ReadRequest函数填写的。
    // 对启用了TLS的连接,本包的HTTP服务器会在调用处理器之前设置TLS字段,否则将设TLS为nil。
    // 客户端会忽略请求中的TLS字段。
    TLS *tls.ConnectionState
}

 

获取请求 URL

通过net/http包中的Request结构体中的字段可以获取请求报文中的信息.Request 结构中的 URL 字段用于表示请求行中包含的 URL,改字段是一个指向 url.URL 结构的指针,让我们来看一下 URL 结构

type URL struct {
    Scheme   string
    Opaque   string    // 编码后的不透明数据
    User     *Userinfo // 用户名和密码信息
    Host     string    // host或host:port
    Path     string
    RawQuery string // 编码后的查询字符串,没有'?'
    Fragment string // 引用的片段(文档位置),没有'#'
}

Path 字段
 获取请求的 URL
 例如:http://luboke.com/hello?username=全栈编程&password=123456
 通过 r.URL.Path 只能得到 /hello
RawQuery 字段
 获取请求的 URL 后面?后面的查询字符串
 例如:http://luboke.com/hello?username=全栈编程&password=123456

 通过 r.URL.RawQuery 得到的是 username=全栈编程&password=123456

获取请求头中的信息

通过 Request 结果中的 Header 字段用来获取请求头中的所有信息,Header 字段 的类型是 Header 类型,而 Header 类型是一个 map[string][]string,string 类型的 key, string 切片类型的值。下面是 Header 类型及它的方法:

type Header
func (h Header) Get(key string) string
func (h Header) Set(key, value string)
func (h Header) Add(key, value string)
func (h Header) Del(key string)
func (h Header) Write(w io.Writer) error
func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error

func (Header) Get

func (h Header) Get(key string) string

Get返回键对应的第一个值,如果键不存在会返回""。如要获取该键对应的值切片,请直接用规范格式的键访问map。

func (Header) Set

func (h Header) Set(key, value string)

Set添加键值对到h,如键已存在则会用只有新值一个元素的切片取代旧值切片。

func (Header) Add

func (h Header) Add(key, value string)

Add添加键值对到h,如键已存在则会将新的值附加到旧值切片后面。

func (Header) Del

func (h Header) Del(key string)

Del删除键值对。

func (Header) Write

func (h Header) Write(w io.Writer) error

Write以有线格式将头域写入w。

获取请求头中的所有信息 r.Header

fmt.Fprintln(w, "请求头中的所有信息有:", r.Header)
fmt.Fprintln(w, "请求头中Accept-Encoding的信息是:", r.Header["Accept-Encoding"])
fmt.Fprintln(w, "请求头中Accept-Encoding的属性值是:", r.Header.Get("Accept-Encoding"))
 
map[User-Agent:[Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36]
Accept:[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,ima
ge/apng,*/*;q=0.8] Accept-Encoding:[gzip, deflate, br] Accept-Language:[zhCN,zh;q=0.9,en-US;q=0.8,en;q=0.7] Connection:[keep-alive] Upgrade-InsecureRequests:[1]]

获取请求头中的某个具体属性的值

假如需要获取 Accept-Encoding 的值

方式一:r.Header[“Accept-Encoding”]
i. 得到的是一个字符串切片
ii. 结果
[gzip, deflate, br]

方式二:r.Header.Get(“Accept-Encoding”)
i. 得到的是字符串形式的值,多个值使用逗号分隔
ii. 结果
gzip, deflate, br

获取请求体中的信息

请求和响应的主体都是有 Request 结构中的 Body 字段表示,这个字段的类型是 io.ReadCloser 接口,该接口包含了 Reader 接口和 Closer 接口,Reader 接口拥有 Read 方法,Closer 接口拥有 Close 方法

type Reader interface {
    Read(p []error)
}

Reader接口用于包装基本的读取方法。

Read方法读取len(p)字节数据写入p。它返回写入的字节数和遇到的任何错误。即使Read方法返回值n < len(p),本方法在被调用时仍可能使用p的全部长度作为暂存空间。如果有部分可用数据,但不够len(p)字节,Read按惯例会返回可以读取到的数据,而不是等待更多数据。

type Writer

type Writer interface {
    Write(p []error)
}

Writer接口用于包装基本的写入方法。

Write方法len(p) 字节数据从p写入底层的数据流。它会返回写入的字节数(0 <= n <= len(p))和遇到的任何导致写入提取结束的错误。Write必须返回非nil的错误,如果它返回的 n < len(p)。Write不能修改切片p中的数据,即使临时修改也不行。

type Closer

type Closer interface {
    Close() error
}

Closer接口用于包装基本的关闭方法。

在第一次调用之后再次被调用时,Close方法的的行为是未定义的。某些实现可能会说明他们自己的行为。

获取请求体中的信息

由于 GET 请求没有请求体,所以我们需要在 HTML 页面中创建一个 form 表单,通 过指定 method=”post”来发送一个 POST 请求

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8080/getBody" method="POST">
    用户名: <input type="text" name="username"
                value="波哥"><br/>
    密 码 : <input type="password" name="password"
                 value="666666"><br/>
    <input type="submit">
</form>

</body>
</html>

package main

import (
	"fmt"
	"net/http"
)

func handler3(w http.ResponseWriter, r *http.Request) {
	//获取内容的长度
	length := r.ContentLength
	//创建一个字节切片
	body := make([]byte, length)
	//读取请求体
	r.Body.Read(body)
	fmt.Fprintln(w, "请求体中的内容是:", string(body))
}
func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/getBody", handler3)
	http.ListenAndServe(":8080", mux)

}

获取请求参数

下面我们就通过 net/http 库中的 Request 结构的字段以及方法获取请求 URL 后面 的请求参数以及 form 表单中提交的请求参数

    • 可以通过Request结构体的Form字段获取PostForm字段获取请求参数
      • Form字段
        • 可以获取Get请求和Post请求中的请求参数
      • PostForm字段
        • 只能获取Post请求表单中的请求参数
      • 在通过Form字段和PostForm字段获取请求参数之前必须要先调用ParseForm方法

Form 字段

类型是 url.Values 类型,Form 是解析好的表单数据,包括 URL 字段的 query 参数和 POST 或 PUT 的表单数据。

type Values map[string][]string

Values将建映射到值的列表。它一般用于查询的参数和表单的属性。不同于http.Header这个字典类型,Values的键是大小写敏感的。

Form 字段只有在调用 Request 的 ParseForm 方法后才有效。在客户端,会忽 略请求中的本字段而使用 Body 替代

func (r *Request) ParseForm() error

ParseForm解析URL中的查询字符串,并将解析结果更新到r.Form字段。

对于POST或PUT请求,ParseForm还会将body当作表单解析,并将结果既更新到r.PostForm也更新到r.Form。解析结果中,POST或PUT请求主体要优先于URL查询字符串(同名变量,主体的值在查询字符串的值前面)。

如果请求的主体的大小没有被MaxBytesReader函数设定限制,其大小默认限制为开头10MB。

ParseMultipartForm会自动调用ParseForm。重复调用本方法是无意义的。

获取 表单中提交的请求参数(username 和 password)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8080/getBody2" method="POST">
    用户名: <input type="text" name="username"
                value="波哥"><br/>
    密 码 : <input type="password" name="password"
                 value="666666"><br/>
    <input type="submit">
</form>

</body>
</html>

注意:在执行 r.Form 之前一定要调用 ParseForm 方法

package main

import (
	"fmt"
	"net/http"
)

func handler4(w http.ResponseWriter, r *http.Request) {
	//解析表单
	r.ParseForm()
	//获取请求参数
	fmt.Fprintln(w, "请求参数为:", r.Form)

        //r.ParseForm()       //解析url传递的参数,对于POST则解析响应包的主体(request body)

	//注意:如果没有调用ParseForm方法,下面无法获取表单的数据
	fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
	fmt.Println("path", r.URL.Path)
	fmt.Println("scheme", r.URL.Scheme)
	fmt.Println(r.Form["url_long"])
	for k, v := range r.Form {
		fmt.Println("key:", k)
		fmt.Println("val:", strings.Join(v, ""))
	}
	fmt.Fprintln(w, "请求参数为:", r.Form) //这个写入到w的是输出到客户端的

    //获取请求参数
    //如果form表单的action属性的URL地址中也有与form表单参数名相同的请求参数,
    //那么参数值都可以得到,并且form表单中的参数值在ULR的参数值的前面
    fmt.Fprintln(w, "请求参数有:", r.Form)
    fmt.Fprintln(w, "POST请求的form表单中的请求参数有:", r.PostForm)

}
func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/getBody2", handler4)
	http.ListenAndServe(":8080", mux)

}

如果对 form 表单做一些修改,在 action 属性的 URL 后面也添加相同的请求参 数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8080/getBody3?username=admin&pwd=123456" method="POST">
    用户名: <input type="text" name="username"
                value="波哥"><br/>
    密 码 : <input type="password" name="password"
                 value="666666"><br/>
    <input type="submit">
</form>

</body>
</html>
package main

import (
	"fmt"
	"net/http"
)

func handler5(w http.ResponseWriter, r *http.Request) {
	//解析表单
	r.ParseForm()
	//获取请求参数
	fmt.Fprintln(w, "请求参数为:", r.Form)
}
func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/getBody3", handler5)
	http.ListenAndServe(":8080", mux)

}

则执行结果如下

表单中的请求参数 username 和 URL 中的请求参数 username 都获取到了,而且表单中的请求参数的值排在 URL 请求参 数值的前面

如果此时我们只想获取表单中的请求参数该怎么办呢?那就需要使 用 Request 结构中的 PostForm 字段

 Request本身也提供了FormValue()函数来获取用户提交的参数。如r.Form["username"]也可写成r.FormValue("username")。调用r.FormValue时会自动调用r.ParseForm,所以不必提前调用。r.FormValue只会返回同名参数中的第一个,若参数不存在则返回空字符串。

r.Form里面包含了所有请求的参数,比如URL中query-string、POST的数据、PUT的数据,所以当你在URL中的query-string字段和POST冲突时,会保存成一个slice,里面存储了多个值,Go官方文档中说在接下来的版本里面将会把POST、GET这些数据分离开来。

PostForm 字段

类型也是 url.Values 类型,用来获取表单中的请求参数 ,此时将 r.Form 改为 r.PostForm 之后的代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8080/getBody4?username=admin&pwd=123456" method="POST">
    用户名: <input type="text" name="username"
                value="波哥"><br/>
    密 码 : <input type="password" name="password"
                 value="666666"><br/>
    <input type="submit">
</form>

</body>
</html>
package main

import (
	"fmt"
	"net/http"
)

func handler6(w http.ResponseWriter, r *http.Request) {
	//解析表单
	r.ParseForm()
	//获取请求参数
	fmt.Fprintln(w, "请求参数为:", r.PostForm)
}
func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/getBody4", handler6)
	http.ListenAndServe(":8080", mux)

}

但是 PostForm 字段只支持 application/x-www-form-urlencoded 编码如果 form 表单的 enctype 属性值为 multipart/form-data,那么使用 PostForm 字段 无法获取表单中的数据,此时需要使用 MultipartForm 字段

说明:form 表单的 enctype 属性的默认值是 application/x-www-formurlencoded 编 码 , 实 现 上 传 文 件 时 需 要 讲 该 属 性 的 值 设 置 为 multipart/form-data 编码格式

 

FormValue方法和PostFormValue方法直接获取请求参数,获取请求参数之前会自动调用ParseForm方法

FormValue 方法

可以通过 FormValue 方法快速地获取某一个请求参数,该方法调用之前 会自动调用 ParseMultipartForm 和 ParseForm 方法对表单进行解析

func (r *Request) FormValue(key string) string

FormValue返回key为键查询r.Form字段得到结果[]string切片的第一个值。POST和PUT主体中的同名参数优先于URL查询字符串。如果必要,本函数会隐式调用ParseMultipartForm和ParseForm。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8080/getBody5?username=admin&pwd=123456" method="POST">
    用户名: <input type="text" name="username"
                value="波哥"><br/>
    密 码 : <input type="password" name="password"
                 value="666666"><br/>
    <input type="submit">
</form>

</body>
</html>
package main

import (
	"fmt"
	"net/http"
)

func handler7(w http.ResponseWriter, r *http.Request) {
	//解析表单
	//r.ParseForm()

	//可以通过 FormValue 方法快速地获取某一个请求参数,该方法调用之前
	//会自动调用 ParseMultipartForm 和 ParseForm 方法对表单进行解析

	//获取请求参数
	fmt.Fprintln(w, "请求参数username的值为:", r.FormValue("username"))
}
func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/getBody5", handler7)
	http.ListenAndServe(":8080", mux)

}

PostFormValue 方法

可以通过 PostFormValue 方法快速地获取表单中的某一个请求参数,该 方法调用之前会自动调用 ParseMultipartForm 和 ParseForm 方法对表单 进行解析

func (r *Request) PostFormValue(key string) string

PostFormValue返回key为键查询r.PostForm字段得到结果[]string切片的第一个值。如果必要,本函数会隐式调用ParseMultipartForm和ParseForm。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8080/getBody6?username=admin&pwd=123456" method="POST">
    用户名: <input type="text" name="username"
                value="波哥"><br/>
    密 码 : <input type="password" name="password"
                 value="666666"><br/>
    <input type="submit">
</form>

</body>
</html>
package main

import (
	"fmt"
	"net/http"
)

func handler8(w http.ResponseWriter, r *http.Request) {
	//解析表单
	//r.ParseForm()

	//可以通过 FormValue 方法快速地获取某一个请求参数,该方法调用之前
	//会自动调用 ParseMultipartForm 和 ParseForm 方法对表单进行解析

	//获取请求参数
	fmt.Fprintln(w, "请求PostFormValue参数username的值为:", r.PostFormValue("username"))
}
func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/getBody6", handler8)
	http.ListenAndServe(":8080", mux)

}

要使表单能够上传文件,首先第一步就是要添加form的enctype属性,enctype属性有如下三种情况:

application/x-www-form-urlencoded   表示在发送前编码所有字符(默认)
multipart/form-data	  不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。
text/plain	  空格转换为 "+" 加号,但不对特殊字符编码。

上传文件

MultipartForm 字段

为了取得 multipart/form-data 编码的表单数据,我们需要用到 Request 结构的 ParseMultipartForm 方法和 MultipartForm 字段,我们通常上传文件时会将 form 表单的 enctype 属性值设置为 multipart/form-data

func (r *Request) ParseMultipartForm(maxMemory int64) error

ParseMultipartForm将请求的主体作为multipart/form-data解析。请求的整个主体都会被解析,得到的文件记录最多maxMemery字节保存在内存,其余部分保存在硬盘的temp文件里。如果必要,ParseMultipartForm会自行调用ParseForm。重复调用本方法是无意义的。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8080/upload" method="POST"
      enctype="multipart/form-data">
    用 户 名 : <input type="text" name="username"
                   value="波哥"><br/>
    文件:<input type="file" name="photo" /><br/>
    <input type="submit">
</form>
</body>
</html>
package main

import (
	"fmt"
	"net/http"
)

func handler9(w http.ResponseWriter, r *http.Request) {
	//解析表单
	r.ParseMultipartForm(1024)
	//打印表单数据
	fmt.Fprintln(w, r.MultipartForm)
}
func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/upload", handler9)
	http.ListenAndServe(":8080", mux)

}

结果中有两个映射,第一个映射映射的是用户名;第二个映射的值是一个地址 

MuiltipartForm 字段的类型为 *multipart.Form,multipart 包下 Form 结构的指 针类型

type FileHeader

type FileHeader struct {
    Filename string
    Header   MIMEHeader
    // 内含隐藏或非导出字段
}

FileHeader描述一个multipart请求的(一个)文件记录的信息。

func (*FileHeader) Open

func (fh *FileHeader) Open() (File, error)

Open方法打开并返回其关联的文件。

打开上传的文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8080/upload" method="POST"
      enctype="multipart/form-data">
    用 户 名 : <input type="text" name="username"
                   value="波哥"><br/>
    文件:<input type="file" name="photo" /><br/>
    <input type="submit">
</form>
</body>
</html>
package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func handler9(w http.ResponseWriter, r *http.Request) {
	//解析表单
	r.ParseMultipartForm(1024)

	//fmt.Fprintln(w, r.MultipartForm)

	fileHeader := r.MultipartForm.File["photo"][0]
	file, err := fileHeader.Open()
	if err == nil {
		data, err := ioutil.ReadAll(file)
		if err == nil {
			//浏览器能打开的则直接显示 ,比如txt
			//浏览器无法打开的则下载 
			fmt.Fprintln(w, string(data))
		}
	}
}
func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/upload", handler9)
	http.ListenAndServe(":8080", mux)

}

FormFile方法

net/http 提供的 FormFile 方法可以快速的获取被上传的文件,但是只能处理上 传一个文件的情况。

func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error)

FormFile返回以key为键查询r.MultipartForm字段得到结果中的第一个文件和它的信息。如果必要,本函数会隐式调用ParseMultipartForm和ParseForm。查询失败会返回ErrMissingFile错误。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8080/upload" method="POST"
      enctype="multipart/form-data">
    用 户 名 : <input type="text" name="username"
                   value="波哥"><br/>
    文件:<input type="file" name="photo" /><br/>
    <input type="submit">
</form>
</body>
</html>

上传文件

func (r *Request) ParseMultipartForm(maxMemory int64) error

ParseMultipartForm将请求的主体作为multipart/form-data解析。请求的整个主体都会被解析,得到的文件记录最多maxMemery字节保存在内存,其余部分保存在硬盘的temp文件里。如果必要,ParseMultipartForm会自行调用ParseForm。重复调用本方法是无意义的。

上传的目标目录不存在则提示

 

通过上面的代码可以看到,处理文件上传我们需要调用r.ParseMultipartForm,里面的参数表示maxMemory最大字节,调用ParseMultipartForm之后,上传的文件存储在maxMemory大小的内存里面,如果文件大小超过了maxMemory,那么剩下的部分将存储在系统的临时文件中。我们可以通过r.FormFile获取上面的文件句柄,然后实例中使用了io.Copy来存储文件。

获取其他非文件字段信息的时候就不需要调用r.ParseForm,因为在需要的时候Go自动会去调用。而且ParseMultipartForm调用一次之后,后面再次调用不会再有效果。

 

通过上面的实例我们可以看到我们上传文件主要三步处理:

  1. 表单中增加enctype="multipart/form-data"
  2. 服务端调用r.ParseMultipartForm,把上传的文件存储在内存和临时文件中
  3. 使用r.FormFile获取文件句柄,然后对文件进行存储等处理。

 

文件handler是multipart.FileHeader,里面存储了如下结构信息

type FileHeader struct {
	Filename string
	Header   textproto.MIMEHeader
	// contains filtered or unexported fields
}

文章作者: 全栈编程@luboke.com
版权声明: 本文章为go语言体系课视频教程配套电子书,版权归 全栈编程@luboke.com所有,欢迎免费学习,转载必须注明出处!但禁止任何商业用途,否则将受到法律制裁!
copyright © 2020 全栈编程@luboke.com