mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
file-server: fix ShowList on root dir
Former-commit-id: 6795382235d76942bcfd31ecc0b4ab02ecb85a8a
This commit is contained in:
parent
0a1b500c8b
commit
57dc64625d
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,3 +5,4 @@ go.sum
|
|||
node_modules
|
||||
/_examples/issue-*/
|
||||
/_examples/feature-*/
|
||||
_examples/**/uploads/*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
66
_examples/file-server/file-server/main.go
Normal file
66
_examples/file-server/file-server/main.go
Normal file
|
@ -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
|
||||
}
|
17
_examples/file-server/file-server/views/upload.html
Normal file
17
_examples/file-server/file-server/views/upload.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Upload Files</title>
|
||||
</head>
|
||||
<body>
|
||||
<form enctype="multipart/form-data"
|
||||
action="/upload" method="POST">
|
||||
<input type="file" name="uploadfile" multiple/> <input type="hidden"
|
||||
name="token" value="{{.}}" />
|
||||
|
||||
<input type="submit" value="upload" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,41 +407,14 @@ func (api *APIBuilder) HandleDir(requestPath, directory string, opts ...DirOptio
|
|||
h = StripPrefix(fullpath, h)
|
||||
}
|
||||
|
||||
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 := 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)
|
||||
}
|
||||
routes = append(routes, api.CreateRoutes([]string{http.MethodGet, http.MethodHead}, requestPath, h)...)
|
||||
|
||||
for _, route := range routes {
|
||||
if route.Method == http.MethodHead {
|
||||
|
@ -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.
|
||||
|
|
|
@ -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("<a href=\"%s\">%s</a>\n", url.String(), htmlReplacer.Replace(name))
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -51,12 +51,6 @@ 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
|
||||
|
||||
// Sitemap properties: https://www.sitemaps.org/protocol.html
|
||||
|
@ -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
|
||||
}
|
||||
|
|
9
iris.go
9
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`.
|
||||
|
|
Loading…
Reference in New Issue
Block a user