Update the _examples/http_request/upload-file to fill the gap, relative discussion: https://github.com/kataras/iris/issues/979

Former-commit-id: a113e8815a6a2821a80ac424f52302528a6e71b5
This commit is contained in:
Gerasimos Maropoulos 2018-04-28 14:48:09 +03:00
parent 3c6d1f2fa3
commit 4eeffd07c7
3 changed files with 94 additions and 18 deletions

View File

@ -11,6 +11,8 @@ import (
"github.com/kataras/iris"
)
const maxSize = 5 << 20 // 5MB
func main() {
app := iris.New()
@ -32,18 +34,72 @@ func main() {
ctx.View("upload_form.html", token)
})
// Handle the post request from the upload_form.html to the server
app.Post("/upload", func(ctx iris.Context) {
// iris.LimitRequestBodySize(32 <<20) as middleware to a route
// or use ctx.SetMaxRequestBodySize(32 << 20)
// to limit the whole request body size,
//
// or let the configuration option at app.Run for global setting
// for POST/PUT methods, including uploads of course.
/* Read before continue.
0. The default post max size is 32MB,
you can extend it to read more data using the `iris.WithPostMaxMemory(maxSize)` configurator at `app.Run`,
note that this will not be enough for your needs, read below.
1. The faster way to check the size is using the `ctx.GetContentLength()` which returns the whole request's size
(plus a logical number like 2MB or even 10MB for the rest of the size like headers). You can create a
middleware to adapt this to any necessary handler.
myLimiter := func(ctx iris.Context) {
if ctx.GetContentLength() > maxSize { // + 2 << 20 {
ctx.StatusCode(iris.StatusRequestEntityTooLarge)
return
}
ctx.Next()
}
app.Post("/upload", myLimiter, myUploadHandler)
Most clients will set the "Content-Length" header (like browsers) but it's always better to make sure that any client
can't send data that your server can't or doesn't want to handle. This can be happen using
the `app.Use(LimitRequestBodySize(maxSize))` (as app or route middleware)
or the `ctx.SetMaxRequestBodySize(maxSize)` to limit the request based on a customized logic inside a particular handler, they're the same,
read below.
2. You can force-limit the request body size inside a handler using the `ctx.SetMaxRequestBodySize(maxSize)`,
this will force the connection to close if the incoming data are larger (most clients will receive it as "connection reset"),
use that to make sure that the client will not send data that your server can't or doesn't want to accept, as a fallback.
app.Post("/upload", iris.LimitRequestBodySize(maxSize), myUploadHandler)
OR
app.Post("/upload", func(ctx iris.Context){
ctx.SetMaxRequestBodySize(maxSize)
// [...]
})
3. Another way is to receive the data and check the second return value's `Size` value of the `ctx.FormFile`, i.e `info.Size`, this will give you
the exact file size, not the whole incoming request data length.
app.Post("/", func(ctx iris.Context){
file, info, err := ctx.FormFile("uploadfile")
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.HTML("Error while uploading: <b>" + err.Error() + "</b>")
return
}
defer file.Close()
if info.Size > maxSize {
ctx.StatusCode(iris.StatusRequestEntityTooLarge)
return
}
// [...]
})
*/
// Handle the post request from the upload_form.html to the server
app.Post("/upload", iris.LimitRequestBodySize(maxSize+1<<20), func(ctx iris.Context) {
// Get the file from the request.
file, info, err := ctx.FormFile("uploadfile")
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.HTML("Error while uploading: <b>" + err.Error() + "</b>")
@ -68,6 +124,6 @@ func main() {
io.Copy(out, file)
})
// start the server at http://localhost:8080 with post limit at 32 MB.
app.Run(iris.Addr(":8080"), iris.WithPostMaxMemory(32<<20))
// start the server at http://localhost:8080 with post limit at 5 MB.
app.Run(iris.Addr(":8080") /* 0.*/, iris.WithPostMaxMemory(maxSize))
}

View File

@ -1,12 +1,15 @@
<html>
<head>
<title>Upload file</title>
<title>Upload file</title>
</head>
<body>
<form enctype="multipart/form-data"
action="http://127.0.0.1:8080/upload" method="POST">
<input type="file" name="uploadfile" /> <input type="hidden"
name="token" value="{{.}}" /> <input type="submit" value="upload" />
<form enctype="multipart/form-data" action="http://127.0.0.1:8080/upload" method="POST">
<input type="file" name="uploadfile" />
<input type="hidden" name="token" value="{{.}}" />
<input type="submit" value="upload" />
</form>
</body>
</html>
</html>

View File

@ -432,6 +432,10 @@ type Context interface {
// which may, setted before with the 'ContentType'.
GetContentType() string
// GetContentLength returns the request's header value of "Content-Length".
// Returns 0 if header was unable to be found or its value was not a valid number.
GetContentLength() int64
// StatusCode sets the status code header to the response.
// Look .GetStatusCode too.
StatusCode(statusCode int)
@ -1597,6 +1601,16 @@ func (ctx *context) GetContentType() string {
return ctx.writer.Header().Get(ContentTypeHeaderKey)
}
// GetContentLength returns the request's header value of "Content-Length".
// Returns 0 if header was unable to be found or its value was not a valid number.
func (ctx *context) GetContentLength() int64 {
if v := ctx.GetHeader(ContentLengthHeaderKey); v != "" {
n, _ := strconv.ParseInt(v, 10, 64)
return n
}
return 0
}
// StatusCode sets the status code header to the response.
// Look .GetStatusCode & .FireStatusCode too.
//
@ -1966,7 +1980,10 @@ func (ctx *context) FormFile(key string) (multipart.File, *multipart.FileHeader,
// here but do it in order to apply the post limit,
// the internal request.FormFile will not do it if that's filled
// and it's not a stream body.
ctx.request.ParseMultipartForm(ctx.Application().ConfigurationReadOnly().GetPostMaxMemory())
if err := ctx.request.ParseMultipartForm(ctx.Application().ConfigurationReadOnly().GetPostMaxMemory()); err != nil {
return nil, nil, err
}
return ctx.request.FormFile(key)
}