Skip to content

Commit

Permalink
Merge branch 'master' into fix_copy_race_condition
Browse files Browse the repository at this point in the history
  • Loading branch information
javierprovecho authored Jul 16, 2017
2 parents b92942f + 93b3a0d commit 88dc66c
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 35 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,10 @@ func main() {
// single file
file, _ := c.FormFile("file")
log.Println(file.Filename)


// Upload the file to specific dst.
// c.SaveUploadedFile(file, dst)

c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
router.Run(":8080")
Expand Down Expand Up @@ -304,6 +307,9 @@ func main() {

for _, file := range files {
log.Println(file.Filename)

// Upload the file to specific dst.
// c.SaveUploadedFile(file, dst)
}
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
})
Expand Down
19 changes: 19 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"net"
"net/http"
"net/url"
"os"
"strings"
"time"

Expand Down Expand Up @@ -435,6 +436,24 @@ func (c *Context) MultipartForm() (*multipart.Form, error) {
return c.Request.MultipartForm, err
}

// SaveUploadedFile uploads the form file to specific dst.
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error {
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()

out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()

io.Copy(out, src)
return nil
}

// Bind checks the Content-Type to select a binding engine automatically,
// Depending the "Content-Type" header different bindings are used:
// "application/json" --> JSON binding
Expand Down
42 changes: 42 additions & 0 deletions context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,18 @@ func TestContextFormFile(t *testing.T) {
if assert.NoError(t, err) {
assert.Equal(t, "test", f.Filename)
}

assert.NoError(t, c.SaveUploadedFile(f, "test"))
}

func TestContextMultipartForm(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
mw.WriteField("foo", "bar")
w, err := mw.CreateFormFile("file", "test")
if assert.NoError(t, err) {
w.Write([]byte("test"))
}
mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", buf)
Expand All @@ -86,6 +92,42 @@ func TestContextMultipartForm(t *testing.T) {
if assert.NoError(t, err) {
assert.NotNil(t, f)
}

assert.NoError(t, c.SaveUploadedFile(f.File["file"][0], "test"))
}

func TestSaveUploadedOpenFailed(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
mw.Close()

c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", buf)
c.Request.Header.Set("Content-Type", mw.FormDataContentType())

f := &multipart.FileHeader{
Filename: "file",
}
assert.Error(t, c.SaveUploadedFile(f, "test"))
}

func TestSaveUploadedCreateFailed(t *testing.T) {
buf := new(bytes.Buffer)
mw := multipart.NewWriter(buf)
w, err := mw.CreateFormFile("file", "test")
if assert.NoError(t, err) {
w.Write([]byte("test"))
}
mw.Close()
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", buf)
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
f, err := c.FormFile("file")
if assert.NoError(t, err) {
assert.Equal(t, "test", f.Filename)
}

assert.Error(t, c.SaveUploadedFile(f, "/"))
}

func TestContextReset(t *testing.T) {
Expand Down
20 changes: 2 additions & 18 deletions examples/upload-file/multiple/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package main

import (
"fmt"
"io"
"net/http"
"os"

"github.com/gin-gonic/gin"
)
Expand All @@ -25,24 +23,10 @@ func main() {
files := form.File["files"]

for _, file := range files {
// Source
src, err := file.Open()
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("file open err: %s", err.Error()))
if err := c.SaveUploadedFile(file, file.Filename); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
return
}
defer src.Close()

// Destination
dst, err := os.Create(file.Filename)
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("Create file err: %s", err.Error()))
return
}
defer dst.Close()

// Copy
io.Copy(dst, src)
}

c.String(http.StatusOK, fmt.Sprintf("Uploaded successfully %d files with fields name=%s and email=%s.", len(files), name, email))
Expand Down
18 changes: 2 additions & 16 deletions examples/upload-file/single/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package main

import (
"fmt"
"io"
"net/http"
"os"

"github.com/gin-gonic/gin"
)
Expand All @@ -22,23 +20,11 @@ func main() {
c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error()))
return
}
src, err := file.Open()
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("file open err: %s", err.Error()))
return
}
defer src.Close()

// Destination
dst, err := os.Create(file.Filename)
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("Create file err: %s", err.Error()))
if err := c.SaveUploadedFile(file, file.Filename); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
return
}
defer dst.Close()

// Copy
io.Copy(dst, src)

c.String(http.StatusOK, fmt.Sprintf("File %s uploaded successfully with fields name=%s and email=%s.", file.Filename, name, email))
})
Expand Down

0 comments on commit 88dc66c

Please sign in to comment.