diff --git a/.gitignore b/.gitignore
index dbec0c72..462f0d0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@ go.sum
node_modules
/_examples/issue-*/
/_examples/feature-*/
+_examples/**/uploads/*
diff --git a/_examples/README.md b/_examples/README.md
index d6833375..8831c8db 100644
--- a/_examples/README.md
+++ b/_examples/README.md
@@ -80,6 +80,7 @@
* [Recovery](recover/main.go)
* [Profiling](pprof/main.go)
* File Server
+ * [File Server](file-server/file-server/main.go)
* [Favicon](file-server/favicon/main.go)
* [Basic](file-server/basic/main.go)
* [Embedding Files Into App Executable File](file-server/embedding-files-into-app/main.go)
diff --git a/_examples/file-server/embedding-files-into-app/main_test.go b/_examples/file-server/embedding-files-into-app/main_test.go
index 57a88e8c..fd8116bb 100644
--- a/_examples/file-server/embedding-files-into-app/main_test.go
+++ b/_examples/file-server/embedding-files-into-app/main_test.go
@@ -78,10 +78,6 @@ func TestEmbeddingFilesIntoApp(t *testing.T) {
t.Fatalf("expected a route to serve embedded files")
}
- if len(route.StaticSites()) > 0 {
- t.Fatalf("not expected a static site, the ./assets directory or its subdirectories do not contain any index.html")
- }
-
if runtime.GOOS != "windows" {
// remove the embedded static favicon for !windows,
// it should be built for unix-specific in order to be work
diff --git a/_examples/file-server/file-server/main.go b/_examples/file-server/file-server/main.go
new file mode 100644
index 00000000..0ce54002
--- /dev/null
+++ b/_examples/file-server/file-server/main.go
@@ -0,0 +1,66 @@
+package main
+
+import (
+ "crypto/md5"
+ "fmt"
+ "io"
+ "mime/multipart"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/kataras/iris/v12"
+)
+
+func init() {
+ os.Mkdir("./uploads", 0700)
+}
+
+func main() {
+ app := iris.New()
+ app.RegisterView(iris.HTML("./views", ".html"))
+
+ app.Get("/", index)
+
+ app.Get("/upload", uploadView)
+ app.Post("/upload", upload)
+
+ app.HandleDir("/files", "./uploads", iris.DirOptions{
+ Gzip: true,
+ ShowList: true,
+ })
+
+ app.Listen(":8080")
+}
+
+func index(ctx iris.Context) {
+ ctx.Redirect("/upload")
+}
+
+func uploadView(ctx iris.Context) {
+ now := time.Now().Unix()
+ h := md5.New()
+ io.WriteString(h, strconv.FormatInt(now, 10))
+ token := fmt.Sprintf("%x", h.Sum(nil))
+
+ ctx.View("upload.html", token)
+}
+
+func upload(ctx iris.Context) {
+ _, err := ctx.UploadFormFiles("./uploads", beforeSave)
+ if err != nil {
+ ctx.StopWithError(iris.StatusInternalServerError, err)
+ return
+ }
+
+ ctx.Redirect("/files")
+}
+
+func beforeSave(ctx iris.Context, file *multipart.FileHeader) {
+ ip := ctx.RemoteAddr()
+ ip = strings.ReplaceAll(ip, ".", "_")
+ ip = strings.ReplaceAll(ip, ":", "_")
+
+ file.Filename = ip + "-" + file.Filename
+}
diff --git a/_examples/file-server/file-server/views/upload.html b/_examples/file-server/file-server/views/upload.html
new file mode 100644
index 00000000..6954b82f
--- /dev/null
+++ b/_examples/file-server/file-server/views/upload.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+ Upload Files
+
+
+
+
+
\ No newline at end of file
diff --git a/context/context.go b/context/context.go
index cb4a0e97..3152cd7f 100644
--- a/context/context.go
+++ b/context/context.go
@@ -444,16 +444,18 @@ type Context interface {
// Header adds a header to the response writer.
Header(name string, value string)
- // ContentType sets the response writer's header key "Content-Type" to the 'cType'.
+ // ContentType sets the response writer's
+ // header "Content-Type" to the 'cType'.
ContentType(cType string)
- // GetContentType returns the response writer's header value of "Content-Type"
- // which may, set before with the 'ContentType'.
+ // GetContentType returns the response writer's
+ // header value of "Content-Type".
GetContentType() string
- // GetContentType returns the request's header value of "Content-Type".
+ // GetContentType returns the request's
+ // trim-ed(without the charset and priority values)
+ // header value of "Content-Type".
GetContentTypeRequested() 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 returns the request's
+ // header value of "Content-Length".
GetContentLength() int64
// StatusCode sets the status code header to the response.
@@ -2256,7 +2258,8 @@ func (ctx *context) contentTypeOnce(cType string, charset string) {
ctx.writer.Header().Set(ContentTypeHeaderKey, cType)
}
-// ContentType sets the response writer's header key "Content-Type" to the 'cType'.
+// ContentType sets the response writer's
+// header "Content-Type" to the 'cType'.
func (ctx *context) ContentType(cType string) {
if cType == "" {
return
@@ -2282,19 +2285,21 @@ func (ctx *context) ContentType(cType string) {
ctx.writer.Header().Set(ContentTypeHeaderKey, cType)
}
-// GetContentType returns the response writer's header value of "Content-Type"
-// which may, set before with the 'ContentType'.
+// GetContentType returns the response writer's
+// header value of "Content-Type".
func (ctx *context) GetContentType() string {
return ctx.writer.Header().Get(ContentTypeHeaderKey)
}
-// GetContentType returns the request's header value of "Content-Type".
+// GetContentType returns the request's
+// trim-ed(without the charset and priority values)
+// header value of "Content-Type".
func (ctx *context) GetContentTypeRequested() string {
return TrimHeaderValue(ctx.GetHeader(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.
+// GetContentLength returns the request's
+// header value of "Content-Length".
func (ctx *context) GetContentLength() int64 {
if v := ctx.GetHeader(ContentLengthHeaderKey); v != "" {
n, _ := strconv.ParseInt(v, 10, 64)
@@ -3427,7 +3432,7 @@ func (ctx *context) TryWriteGzip(b []byte) (int, error) {
n, err := ctx.WriteGzip(b)
if err != nil {
// check if the error came from gzip not allowed and not the writer itself
- if errors.Is(err, ErrGzipNotSupported) {
+ if err == ErrGzipNotSupported {
// client didn't supported gzip, write them uncompressed:
return ctx.writer.Write(b)
}
@@ -4239,6 +4244,8 @@ type N struct {
Other []byte // custom content types.
}
+var _ ContentSelector = N{}
+
// SelectContent returns a content based on the matched negotiated "mime".
func (n N) SelectContent(mime string) interface{} {
switch mime {
@@ -4419,7 +4426,6 @@ func (ctx *context) Negotiate(v interface{}) (int, error) {
ctx.StatusCode(http.StatusNotAcceptable)
return -1, ErrContentNotSupported
}
-
}
}
diff --git a/context/route.go b/context/route.go
index dcce5f36..56104828 100644
--- a/context/route.go
+++ b/context/route.go
@@ -2,10 +2,6 @@ package context
import (
"io"
- "os"
- "path"
- "path/filepath"
- "strings"
"time"
"github.com/kataras/iris/v12/macro"
@@ -66,13 +62,6 @@ type RouteReadOnly interface {
// MainHandlerIndex returns the first registered handler's index for the route.
MainHandlerIndex() int
- // StaticSites if not empty, refers to the system (or virtual if embedded) directory
- // and sub directories that this "GET" route was registered to serve files and folders
- // that contain index.html (a site). The index handler may registered by other
- // route, manually or automatic by the framework,
- // get the route by `Application#GetRouteByPath(staticSite.RequestPath)`.
- StaticSites() []StaticSite
-
// Sitemap properties: https://www.sitemaps.org/protocol.html
// GetLastMod returns the date of last modification of the file served by this route.
@@ -82,51 +71,3 @@ type RouteReadOnly interface {
// GetPriority returns the priority of this route's URL relative to other URLs on your site.
GetPriority() float32
}
-
-// StaticSite is a structure which is used as field on the `Route`
-// and route registration on the `APIBuilder#HandleDir`.
-// See `GetStaticSites` and `APIBuilder#HandleDir`.
-type StaticSite struct {
- Dir string `json:"dir"`
- RequestPath string `json:"requestPath"`
-}
-
-// GetStaticSites search for a relative filename of "indexName" in "rootDir" and all its subdirectories
-// and returns a list of structures which contains the directory found an "indexName" and the request path
-// that a route should be registered to handle this "indexName".
-// The request path is given by the directory which an index exists on.
-func GetStaticSites(rootDir, rootRequestPath, indexName string) (sites []StaticSite) {
- f, err := os.Open(rootDir)
- if err != nil {
- return nil
- }
-
- list, err := f.Readdir(-1)
- f.Close()
- if err != nil {
- return nil
- }
-
- if len(list) == 0 {
- return nil
- }
-
- for _, l := range list {
- dir := filepath.Join(rootDir, l.Name())
-
- if l.IsDir() {
- sites = append(sites, GetStaticSites(dir, path.Join(rootRequestPath, l.Name()), indexName)...)
- continue
- }
-
- if l.Name() == strings.TrimPrefix(indexName, "/") {
- sites = append(sites, StaticSite{
- Dir: filepath.FromSlash(rootDir),
- RequestPath: rootRequestPath,
- })
- continue
- }
- }
-
- return
-}
diff --git a/core/router/api_builder.go b/core/router/api_builder.go
index 06cd4fb8..678eb062 100644
--- a/core/router/api_builder.go
+++ b/core/router/api_builder.go
@@ -390,12 +390,11 @@ func (api *APIBuilder) HandleMany(methodOrMulti string, relativePathorMulti stri
//
// api.HandleDir("/static", "./assets", DirOptions {ShowList: true, Gzip: true, IndexName: "index.html"})
//
-// Returns the GET *Route.
+// Returns all the registered routes, including GET index and path patterm and HEAD.
//
// Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/file-server
-func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptions) (getRoute *Route) {
+func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptions) (routes []*Route) {
options := getDirOptions(opts...)
-
h := FileServer(directory, options)
description := directory
fileName, lineNumber := context.HandlerFileLine(h) // take those before StripPrefix.
@@ -408,42 +407,15 @@ func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptio
h = StripPrefix(fullpath, h)
}
- requestPath = joinPath(requestPath, WildcardFileParam())
- routes := api.CreateRoutes([]string{http.MethodGet, http.MethodHead}, requestPath, h)
- getRoute = routes[0]
- // we get all index, including sub directories even if those
- // are already managed by the static handler itself.
- staticSites := context.GetStaticSites(directory, getRoute.StaticPath(), options.IndexName)
- for _, s := range staticSites {
- // if the end-dev did manage that index route manually already
- // then skip the auto-registration.
- //
- // Also keep note that end-dev is still able to replace this route and manage by him/herself
- // later on by a simple `Handle/Get/` call, refer to `repository#register`.
- if api.GetRouteByPath(s.RequestPath) != nil {
- continue
- }
-
- if n := len(api.relativePath); n > 0 && api.relativePath[n-1] == SubdomainPrefix[0] {
- // this api is a subdomain-based.
- slashIdx := strings.IndexByte(s.RequestPath, '/')
- if slashIdx == -1 {
- slashIdx = 0
- }
-
- requestPath = s.RequestPath[slashIdx:]
- } else {
- requestPath = s.RequestPath[strings.Index(s.RequestPath, api.relativePath)+len(api.relativePath):]
- }
-
- if requestPath == "" {
- requestPath = "/"
- }
-
- routes = append(routes, api.CreateRoutes([]string{http.MethodGet}, requestPath, h)...)
- getRoute.StaticSites = append(getRoute.StaticSites, s)
+ if api.GetRouteByPath(fullpath) == nil {
+ // register index if not registered by the end-developer.
+ routes = api.CreateRoutes([]string{http.MethodGet, http.MethodHead}, requestPath, h)
}
+ requestPath = joinPath(requestPath, WildcardFileParam())
+
+ routes = append(routes, api.CreateRoutes([]string{http.MethodGet, http.MethodHead}, requestPath, h)...)
+
for _, route := range routes {
if route.Method == http.MethodHead {
} else {
@@ -457,7 +429,7 @@ func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptio
}
}
- return getRoute
+ return routes
}
// CreateRoutes returns a list of Party-based Routes.
diff --git a/core/router/fs.go b/core/router/fs.go
index c9010362..1fd0bd95 100644
--- a/core/router/fs.go
+++ b/core/router/fs.go
@@ -316,10 +316,22 @@ func FileServer(directory string, opts ...DirOptions) context.Handler {
if d.IsDir() {
name += "/"
}
+
+ upath := ""
+ // if ctx.Path() == "/" && dirName == strings.TrimPrefix(directory, "./") {
+ if ctx.Path() == "/" {
+ upath = ctx.GetCurrentRoute().StaticPath() + "/" + name
+ } else {
+ upath = "./" + dirName + "/" + name
+ }
+
+ url := url.URL{
+ Path: upath,
+ } // edit here to redirect correctly, standard library misses that.
+
// name may contain '?' or '#', which must be escaped to remain
// part of the URL path, and not indicate the start of a query
// string or fragment.
- url := url.URL{Path: joinPath("./"+dirName, name)} // edit here to redirect correctly, standard library misses that.
_, err = ctx.Writef("%s\n", url.String(), htmlReplacer.Replace(name))
if err != nil {
return err
diff --git a/core/router/party.go b/core/router/party.go
index 8efb92df..73028d5e 100644
--- a/core/router/party.go
+++ b/core/router/party.go
@@ -152,14 +152,14 @@ type Party interface {
// second parameter : the system or the embedded directory that needs to be served
// third parameter : not required, the directory options, set fields is optional.
//
- // for more options look router.FileServer.
+ // Alternatively, to get just the handler for that look the FileServer function instead.
//
// api.HandleDir("/static", "./assets", DirOptions {ShowList: true, Gzip: true, IndexName: "index.html"})
//
- // Returns the GET *Route.
+ // Returns all the registered routes, including GET index and path patterm and HEAD.
//
// Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/file-server
- HandleDir(requestPath, directory string, opts ...DirOptions) *Route
+ HandleDir(requestPath, directory string, opts ...DirOptions) []*Route
// None registers an "offline" route
// see context.ExecRoute(routeName) and
diff --git a/core/router/route.go b/core/router/route.go
index ac5dc9f2..6b64b1d5 100644
--- a/core/router/route.go
+++ b/core/router/route.go
@@ -51,13 +51,7 @@ type Route struct {
RegisterFileName string `json:"registerFileName"`
RegisterLineNumber int `json:"registerLineNumber"`
- // StaticSites if not empty, refers to the system (or virtual if embedded) directory
- // and sub directories that this "GET" route was registered to serve files and folders
- // that contain index.html (a site). The index handler may registered by other
- // route, manually or automatic by the framework,
- // get the route by `Application#GetRouteByPath(staticSite.RequestPath)`.
- StaticSites []context.StaticSite `json:"staticSites"`
- topLink *Route
+ topLink *Route
// Sitemap properties: https://www.sitemaps.org/protocol.html
LastMod time.Time `json:"lastMod,omitempty"`
@@ -521,10 +515,6 @@ func (rd routeReadOnlyWrapper) MainHandlerIndex() int {
return rd.Route.MainHandlerIndex
}
-func (rd routeReadOnlyWrapper) StaticSites() []context.StaticSite {
- return rd.Route.StaticSites
-}
-
func (rd routeReadOnlyWrapper) GetLastMod() time.Time {
return rd.Route.LastMod
}
diff --git a/iris.go b/iris.go
index 0b6a6e1e..e8aa12d9 100644
--- a/iris.go
+++ b/iris.go
@@ -212,6 +212,9 @@ func Default() *Application {
app := New()
app.Use(recover.New())
app.Use(requestLogger.New())
+ app.Use(Gzip)
+ app.Use(GzipReader)
+
app.defaultMode = true
return app
@@ -615,6 +618,12 @@ var (
//
// A shortcut for the `context#ErrPushNotSupported`.
ErrPushNotSupported = context.ErrPushNotSupported
+ // ErrGzipNotSupported may be returned from
+ // `WriteGzip` and `GzipReader` methods if
+ // the client does not support the "gzip" compression.
+ //
+ // A shortcut for the `context#ErrGzipNotSupported`.
+ ErrGzipNotSupported = context.ErrGzipNotSupported
)
// Constants for input argument at `router.RouteRegisterRule`.