Golang Web应用中按F5重新提交文件时遇到的问题
Golang Web应用中按F5重新提交文件时遇到的问题 大家好,我是Go语言的新手。在提交表单后按F5刷新时遇到问题,出现以下错误信息:
2018/08/06 12:20:50 multipart: NextPart: EOF
2018/08/06 12:20:50 http: panic serving [::1]:5433: runtime error: invalid memory address or nil pointer dereference
PS D:\Golang\src\github.com\deepaksinghkushwah\upload-test> go run main.go
2018/08/06 12:25:19 multipart: NextPart: EOF
2018/08/06 12:25:19 http: panic serving [::1]:5506: runtime error: invalid memory address or nil pointer dereference
goroutine 4 [running]:
net/http.(*conn).serve.func1(0xc04216e000)
C:/Go/src/net/http/server.go:1726 +0xd7
panic(0x715740, 0x948b50)
C:/Go/src/runtime/panic.go:502 +0x237
main.uploadhandler(0x7ba1a0, 0xc042176000, 0xc04212e100)
D:/Golang/src/github.com/deepaksinghkushwah/upload-test/main.go:46 +0x29a
net/http.HandlerFunc.ServeHTTP(0x78ee00, 0x7ba1a0, 0xc042176000, 0xc04212e100)
C:/Go/src/net/http/server.go:1947 +0x4b
net/http.(*ServeMux).ServeHTTP(0x957920, 0x7ba1a0, 0xc042176000, 0xc04212e100)
C:/Go/src/net/http/server.go:2337 +0x137
net/http.serverHandler.ServeHTTP(0xc0421620d0, 0x7ba1a0, 0xc042176000, 0xc04212e100)
C:/Go/src/net/http/server.go:2694 +0xc3
net/http.(*conn).serve(0xc04216e000, 0x7ba360, 0xc042018200)
C:/Go/src/net/http/server.go:1830 +0x658
created by net/http.(*Server).Serve
C:/Go/src/net/http/server.go:2795 +0x282
以下是我的代码:
package main
import (
"fmt"
"html/template"
"io"
"log"
"net/http"
"os"
"strings"
"github.com/satori/go.uuid"
)
var tpl *template.Template
// Page struct which describe page structure
type Page struct {
Title string
Keywords string
Description string
Message string
}
func init() {
tpl = template.Must(template.ParseGlob("templates/*.html"))
}
func homehandler(w http.ResponseWriter, r *http.Request) {
page := Page{Title: "Home Page", Keywords: "This is home page keywords", Description: "This is description of page"}
err := tpl.ExecuteTemplate(w, "index.html", page)
checkWebError(w, err)
}
func uploadhandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
r.ParseMultipartForm(32 << 20)
file, handle, err := r.FormFile("image")
checkError(err)
// we will use this id for new unique name
id, err := uuid.NewV4()
// extension will come with . (dot) extension
ext := strings.Index(handle.Filename, ".")
newname := fmt.Sprintf("%s", id) + handle.Filename[ext:]
defer file.Close()
f, err := os.OpenFile("./images/"+newname, os.O_WRONLY|os.O_CREATE, 0777)
checkError(err)
defer f.Close()
io.Copy(f, file)
}
page := Page{Title: "File Uploaded", Keywords: "This is home page keywords", Description: "This is description of page", Message: "File Uploaded"}
err := tpl.ExecuteTemplate(w, "index.html", page)
checkWebError(w, err)
}
func checkWebError(w http.ResponseWriter, error error) {
if error != nil {
log.Println(error)
w.WriteHeader(http.StatusInternalServerError)
return
}
}
func checkError(error error) {
if error != nil {
log.Println(error)
return
}
}
func main() {
http.HandleFunc("/", homehandler)
http.HandleFunc("/upload", uploadhandler)
http.ListenAndServe(":8080", nil)
}
请帮助我并告诉我如何解决这个问题。
更多关于Golang Web应用中按F5重新提交文件时遇到的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于Golang Web应用中按F5重新提交文件时遇到的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这个问题的根本原因是当用户按F5刷新页面时,浏览器会重新提交之前的POST请求,但由于此时没有实际的文件数据,导致r.FormFile("image")返回错误,进而引发空指针解引用。
以下是修复后的代码:
package main
import (
"fmt"
"html/template"
"io"
"log"
"net/http"
"os"
"strings"
"github.com/satori/go.uuid"
)
var tpl *template.Template
// Page struct which describe page structure
type Page struct {
Title string
Keywords string
Description string
Message string
}
func init() {
tpl = template.Must(template.ParseGlob("templates/*.html"))
}
func homehandler(w http.ResponseWriter, r *http.Request) {
page := Page{Title: "Home Page", Keywords: "This is home page keywords", Description: "This is description of page"}
err := tpl.ExecuteTemplate(w, "index.html", page)
checkWebError(w, err)
}
func uploadhandler(w http.ResponseWriter, r *http.Request) {
var message string
if r.Method == "POST" {
// 检查是否是multipart表单
if strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") {
err := r.ParseMultipartForm(32 << 20)
if err != nil {
log.Printf("Error parsing multipart form: %v", err)
message = "Error parsing form data"
} else {
file, handle, err := r.FormFile("image")
if err != nil {
log.Printf("Error getting form file: %v", err)
message = "No file uploaded or file error"
} else {
defer file.Close()
// 验证文件大小
if handle.Size == 0 {
message = "Empty file uploaded"
} else {
// we will use this id for new unique name
id, err := uuid.NewV4()
if err != nil {
log.Printf("Error generating UUID: %v", err)
message = "Error generating file name"
} else {
// extension will come with . (dot) extension
ext := strings.Index(handle.Filename, ".")
if ext == -1 {
message = "Invalid file name"
} else {
newname := fmt.Sprintf("%s", id) + handle.Filename[ext:]
f, err := os.OpenFile("./images/"+newname, os.O_WRONLY|os.O_CREATE, 0777)
if err != nil {
log.Printf("Error creating file: %v", err)
message = "Error saving file"
} else {
defer f.Close()
_, err = io.Copy(f, file)
if err != nil {
log.Printf("Error copying file: %v", err)
message = "Error saving file"
} else {
message = "File Uploaded Successfully"
}
}
}
}
}
}
}
} else {
message = "Invalid form content type"
}
} else {
message = "Please upload a file"
}
page := Page{
Title: "File Upload",
Keywords: "This is home page keywords",
Description: "This is description of page",
Message: message,
}
err := tpl.ExecuteTemplate(w, "index.html", page)
checkWebError(w, err)
}
func checkWebError(w http.ResponseWriter, error error) {
if error != nil {
log.Println(error)
w.WriteHeader(http.StatusInternalServerError)
return
}
}
func checkError(error error) {
if error != nil {
log.Println(error)
return
}
}
func main() {
http.HandleFunc("/", homehandler)
http.HandleFunc("/upload", uploadhandler)
http.ListenAndServe(":8080", nil)
}
主要修改包括:
- 添加了对
Content-Type的检查,确保是有效的multipart表单 - 对
r.ParseMultipartForm()和r.FormFile()的错误进行了单独处理 - 添加了文件大小验证
- 对所有可能出错的步骤都进行了错误处理
- 使用变量
message来存储操作结果信息,避免在出错时直接panic
这样当用户按F5刷新时,代码会优雅地处理重新提交的请求,显示相应的错误信息而不是panic。

