diff --git a/en/04.5.md b/en/04.5.md index 25803e49..bbc62ef7 100644 --- a/en/04.5.md +++ b/en/04.5.md @@ -4,58 +4,67 @@ Suppose you have a website like Instagram and you want users to upload their bea You have to add property `enctype` to the form that you want to use for uploading photos. There are three possible values for this property: - application/x-www-form-urlencoded Transcode all characters before uploading (default). - multipart/form-data No transcoding. You must use this value when your form has file upload controls. - text/plain Convert spaces to "+", but no transcoding for special characters. +``` +application/x-www-form-urlencoded Transcode all characters before uploading (default). +multipart/form-data No transcoding. You must use this value when your form has file upload controls. +text/plain Convert spaces to "+", but no transcoding for special characters. +``` + Therefore, the HTML content of a file upload form should look like this: - - - Upload file - - -
- - - -
- - +``` + + + Upload file + + +
+ + + +
+ + +``` + We need to add a function on the server side to handle this form. - http.HandleFunc("/upload", upload) +``` +http.HandleFunc("/upload", upload) - // upload logic - func upload(w http.ResponseWriter, r *http.Request) { - fmt.Println("method:", r.Method) - if r.Method == "GET" { - crutime := time.Now().Unix() - h := md5.New() - io.WriteString(h, strconv.FormatInt(crutime, 10)) - token := fmt.Sprintf("%x", h.Sum(nil)) +// upload logic +func upload(w http.ResponseWriter, r *http.Request) { + fmt.Println("method:", r.Method) + if r.Method == "GET" { + crutime := time.Now().Unix() + h := md5.New() + io.WriteString(h, strconv.FormatInt(crutime, 10)) + token := fmt.Sprintf("%x", h.Sum(nil)) + + t, _ := template.ParseFiles("upload.gtpl") + t.Execute(w, token) + } else { + r.ParseMultipartForm(32 << 20) + file, handler, err := r.FormFile("uploadfile") + if err != nil { + fmt.Println(err) + return + } + defer file.Close() + fmt.Fprintf(w, "%v", handler.Header) + f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + fmt.Println(err) + return + } + defer f.Close() + io.Copy(f, file) + } +} +``` - t, _ := template.ParseFiles("upload.gtpl") - t.Execute(w, token) - } else { - r.ParseMultipartForm(32 << 20) - file, handler, err := r.FormFile("uploadfile") - if err != nil { - fmt.Println(err) - return - } - defer file.Close() - fmt.Fprintf(w, "%v", handler.Header) - f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - fmt.Println(err) - return - } - defer f.Close() - io.Copy(f, file) - } - } As you can see, we need to call `r.ParseMultipartForm` for uploading files. The function `maxMemory` argument. After you call `ParseMultipartForm`, the file will be saved in the server memory with `maxMemory` size. If the file size is larger than `maxMemory`, the rest of the data will be saved in a system temporary file. You can use `r.FormFile` to get the file handle and use `io.Copy` to save to your file system. @@ -69,11 +78,13 @@ We use three steps for uploading files as follows: The file handler is the `multipart.FileHeader`. It uses the following struct: - type FileHeader struct { - Filename string - Header textproto.MIMEHeader - // contains filtered or unexported fields - } +``` +type FileHeader struct { + Filename string + Header textproto.MIMEHeader + // contains filtered or unexported fields +} +``` ![](images/4.5.upload2.png?raw=true) @@ -83,65 +94,68 @@ Figure 4.5 Print information on server after receiving file. I showed an example of using a form to a upload a file. We can impersonate a client form to upload files in Go as well. - package main +``` +package main - import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "mime/multipart" - "net/http" - "os" - ) +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "os" +) - func postFile(filename string, targetUrl string) error { - bodyBuf := &bytes.Buffer{} - bodyWriter := multipart.NewWriter(bodyBuf) - - // this step is very important - fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename) - if err != nil { - fmt.Println("error writing to buffer") - return err - } - - // open file handle - fh, err := os.Open(filename) - if err != nil { - fmt.Println("error opening file") - return err - } - - //iocopy - _, err = io.Copy(fileWriter, fh) - if err != nil { - return err - } - - contentType := bodyWriter.FormDataContentType() - bodyWriter.Close() - - resp, err := http.Post(targetUrl, contentType, bodyBuf) - if err != nil { - return err - } - defer resp.Body.Close() - resp_body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - fmt.Println(resp.Status) - fmt.Println(string(resp_body)) - return nil - } +func postFile(filename string, targetUrl string) error { + bodyBuf := &bytes.Buffer{} + bodyWriter := multipart.NewWriter(bodyBuf) + + // this step is very important + fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename) + if err != nil { + fmt.Println("error writing to buffer") + return err + } + + // open file handle + fh, err := os.Open(filename) + if err != nil { + fmt.Println("error opening file") + return err + } + + //iocopy + _, err = io.Copy(fileWriter, fh) + if err != nil { + return err + } + + contentType := bodyWriter.FormDataContentType() + bodyWriter.Close() + + resp, err := http.Post(targetUrl, contentType, bodyBuf) + if err != nil { + return err + } + defer resp.Body.Close() + resp_body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + fmt.Println(resp.Status) + fmt.Println(string(resp_body)) + return nil +} + +// sample usage +func main() { + target_url := "http://localhost:9090/upload" + filename := "./astaxie.pdf" + postFile(filename, target_url) +} +``` - // sample usage - func main() { - target_url := "http://localhost:9090/upload" - filename := "./astaxie.pdf" - postFile(filename, target_url) - } The above example shows you how to use a client to upload files. It uses `multipart.Write` to write files into cache and sends them to the server through the POST method.