Go語(yǔ)言使用net/http實(shí)現(xiàn)簡(jiǎn)單登錄驗(yàn)證和文件上傳功能
最近再看Go語(yǔ)言web編程,go語(yǔ)言搭建Web服務(wù)器,既可以用go原生的net/http包,也可以用gin/fasthttp/fiber等這些Web框架。本博客使用net/http模塊編寫了一個(gè)簡(jiǎn)單的登錄驗(yàn)證和文件上傳的功能,在此做個(gè)簡(jiǎn)單記錄。
代碼如下:
package main import ( "fmt" "html/template" "io" "log" "net/http" "os" ) /* go運(yùn)行方式: (1)解釋運(yùn)行 go run main.go (2)編譯運(yùn)行 --使用默認(rèn)名 go build main.go ./main --指定可執(zhí)行程序名 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,調(diào)用ParseMultipart后,上傳的文件存儲(chǔ)在maxMemory大小的內(nèi)存中, //如果大小超過(guò)maxMemory,剩下部分存儲(chǔ)在系統(tǒng)的臨時(shí)文件中 r.ParseMultipartForm(32 << 10) //根據(jù)input中的name="uploadfile"來(lái)獲得上傳的文件句柄 file, header, err := r.FormFile("uploadfile") if err != nil { fmt.Fprintf(writer, "上傳出錯(cuò)") 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")) // 將文件保存到服務(wù)器指定的目錄(* 用來(lái)隨機(jī)數(shù)的占位符) 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, "上傳出錯(cuò)") 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() //監(jiān)聽(tīng)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, // 對(duì)應(yīng)DefaultServeMux路由 } server.ListenAndServe() } func main() { main2() }
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"/> <title>歡迎進(jìn)入首頁(yè)</title> </head> <body> <h3>登錄測(cè)試</h3> <hr/> <form action="http://localhost:8181/login" method="post"> <table border=0 title="測(cè)試"> <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>文件上傳測(cè)試</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.文件目錄結(jié)構(gòu)
2.編譯運(yùn)行
3.用戶登錄
http://127.0.0.1:8181/login
4.文件上傳
5.mime/multipart模擬form表單上傳文件
使用mime/multipart包,可以將multipart/form-data數(shù)據(jù)解析為一組文件和表單字段,或者使用multipart.Writer將文件和表單字段寫入HTTP請(qǐng)求體中。
以下例子中首先打開(kāi)要上傳的文件,然后創(chuàng)建一個(gè)multipart.Writer,用于構(gòu)造multipart/form-data格式的請(qǐng)求體。我們使用CreateFormFile方法創(chuàng)建一個(gè)multipart.Part,用于表示文件字段,將文件內(nèi)容復(fù)制到該P(yáng)art中。我們還使用WriteField方法添加其他表單字段。然后,我們關(guān)閉multipart.Writer,以便寫入Content-Type和boundary,并使用NewRequest方法創(chuàng)建一個(gè)HTTP請(qǐng)求。我們將Content-Type設(shè)置為multipart/form-data,并使用默認(rèn)的HTTP客戶端發(fā)送請(qǐng)求。最后,我們讀取并處理響應(yīng)。
關(guān)于什么是multipart/form-data?multipart/form-data的基礎(chǔ)是post請(qǐng)求,即基于post請(qǐng)求來(lái)實(shí)現(xiàn)的multipart/form-data形式的post與普通post請(qǐng)求的不同之處體現(xiàn)在請(qǐng)求頭,請(qǐng)求體2個(gè)部分1)請(qǐng)求頭:必須包含Content-Type信息,且其值也必須規(guī)定為multipart/form-data,同時(shí)還需要規(guī)定一個(gè)內(nèi)容分割符用于分割請(qǐng)求體中不同參數(shù)的內(nèi)容(普通post請(qǐng)求的參數(shù)分割符默認(rèn)為&,參數(shù)與參數(shù)值的分隔符為=)。具體的頭信息格式如下:Content-Type: multipart/form-data; boundary=${bound}其中${bound} 是一個(gè)占位符,代表我們規(guī)定的具體分割符;可以自己任意規(guī)定,但為了避免和正常文本重復(fù)了,盡量要使用復(fù)雜一點(diǎn)的內(nèi)容。如:—0016e68ee29c5d515f04cedf6733比如有一個(gè)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)請(qǐng)求體:它也是一個(gè)字符串,不過(guò)和普通post請(qǐng)求體不同的是它的構(gòu)造方式。普通post請(qǐng)求體是簡(jiǎn)單的鍵值對(duì)連接,格式如下:k1=v1&k2=v2&k3=v3而multipart/form-data則是添加了分隔符、參數(shù)描述信息等內(nèi)容的構(gòu)造體。具體格式如下:--${bound}Content-Disposition: form-data; name="Filename" //第一個(gè)參數(shù),相當(dāng)于k1;然后回車;然后是參數(shù)的值,即v1HTTP.pdf //參數(shù)值v1--${bound} //其實(shí)${bound}就相當(dāng)于上面普通post請(qǐng)求體中的&的作用Content-Disposition: form-data; name="file000"; filename="HTTP協(xié)議詳解.pdf" //這里說(shuō)明傳入的是文件,下面是文件提Content-Type: application/octet-stream //傳入文件類型,如果傳入的是.jpg,則這里會(huì)是image/jpeg %PDF-1.5file content%%EOF--${bound}Content-Disposition: form-data; name="Upload"Submit Query--${bound}--都是以${bound}為開(kāi)頭的,并且最后一個(gè)${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" // 打開(kāi)要上傳的文件 file, err := os.Open(filePath) if err != nil { fmt.Println("Failed to open file:", err) return } defer file.Close() // 創(chuàng)建multipart.Writer,用于構(gòu)造multipart/form-data格式的請(qǐng)求體 var requestBody bytes.Buffer multipartWriter := multipart.NewWriter(&requestBody) // 創(chuàng)建一個(gè)multipart.Part,用于表示文件字段 part, err := multipartWriter.CreateFormFile("uploadfile", filepath.Base(filePath)) if err != nil { fmt.Println("Failed to create form file:", err) return } // 將文件內(nèi)容復(fù)制到multipart.Part中 _, err = io.Copy(part, file) if err != nil { fmt.Println("Failed to copy file content:", err) return } // 添加其他表單字段 multipartWriter.WriteField("title", "My file") // 關(guān)閉multipart.Writer,以便寫入Content-Type和boundary err = multipartWriter.Close() if err != nil { fmt.Println("Failed to close multipart writer:", err) return } // 創(chuàng)建HTTP請(qǐng)求 req, err := http.NewRequest("POST", "http://127.0.0.1:8181/upload", &requestBody) if err != nil { fmt.Println("Failed to create request:", err) return } // 設(shè)置Content-Type為multipart/form-data req.Header.Set("Content-Type", multipartWriter.FormDataContentType()) // 發(fā)送HTTP請(qǐng)求 client := http.DefaultClient resp, err := client.Do(req) if err != nil { fmt.Println("Failed to send request:", err) return } defer resp.Body.Close() // 處理響應(yīng) respBody, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("Failed to read response:", err) return } fmt.Println("Response:", string(respBody)) }
編譯執(zhí)行:
go build test.go
./test
運(yùn)行結(jié)果展示:
到此這篇關(guān)于Go語(yǔ)言使用net/http實(shí)現(xiàn)簡(jiǎn)單登錄驗(yàn)證和文件上傳功能的文章就介紹到這了,更多相關(guān)Go登錄驗(yàn)證和文件上傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang使用協(xié)程實(shí)現(xiàn)批量獲取數(shù)據(jù)
服務(wù)端經(jīng)常需要返回一個(gè)列表,里面包含很多用戶數(shù)據(jù),常規(guī)做法當(dāng)然是遍歷然后讀緩存。使用Go語(yǔ)言后,可以并發(fā)獲取,極大提升效率,本文就來(lái)聊聊具體的實(shí)現(xiàn)方法,希望對(duì)大家有所幫助2023-02-02Goland IDEA項(xiàng)目多開(kāi)設(shè)置方式
這篇文章主要介紹了Goland IDEA項(xiàng)目多開(kāi)設(shè)置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12golang 生成對(duì)應(yīng)的數(shù)據(jù)表struct定義操作
這篇文章主要介紹了golang 生成對(duì)應(yīng)的數(shù)據(jù)表struct定義操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04Go語(yǔ)言集成開(kāi)發(fā)環(huán)境之VS Code安裝使用
VS Code是微軟開(kāi)源的一款編輯器,插件系統(tǒng)十分的豐富,下面介紹如何用VS Code搭建go語(yǔ)言開(kāi)發(fā)環(huán)境,需要的朋友可以參考下2021-10-10使用go實(shí)現(xiàn)刪除sql里面的注釋和字符串功能(demo)
這篇文章主要介紹了使用go實(shí)現(xiàn)刪除sql里面的注釋和字符串功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11