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)
}

主要修改包括:

  1. 添加了对Content-Type的检查,确保是有效的multipart表单
  2. r.ParseMultipartForm()r.FormFile()的错误进行了单独处理
  3. 添加了文件大小验证
  4. 对所有可能出错的步骤都进行了错误处理
  5. 使用变量message来存储操作结果信息,避免在出错时直接panic

这样当用户按F5刷新时,代码会优雅地处理重新提交的请求,显示相应的错误信息而不是panic。

回到顶部