Listing
By default Iris will not list files and sub-directories when client requests a path of a directory (e.g. http://localhost:8080/files/folder). To enable file listing you just set DirOptions.ShowList to true:
1
options := iris.DirOptions{
2
// [...]
3
ShowList: true,
4
}
5
6
app.HandleDir("/files", iris.Dir("./uploads"), options)
Copied!
By default listing is rendering a simple page of <a href html links, like the standard net/http package does. To change the default behavior use the DirOptions.DirList field, which accepts a function of form:
1
type DirListFunc func(ctx iris.Context, dirOptions iris.DirOptions, dirName string, dir http.File) error
Copied!
1
options := iris.DirOptions{
2
// [...]
3
ShowList: true,
4
DirList: func(ctx iris.Context, dirOptions iris.DirOptions, dirName string, dir http.File) error {
5
// [...]
6
},
7
}
Copied!
The DirListRich is a function helper for a better look & feel. It's a DirListFunc.
1
func DirListRich(opts ...DirListRichOptions) DirListFunc
Copied!
The DirListRich function can optionally accept DirListRichOptions:
1
type DirListRichOptions struct {
2
// If not nil then this template's "dirlist" is used to render the listing page.
3
Tmpl *template.Template // * "html/template"
4
// If not empty then this view file is used to render the listing page.
5
// The view should be registered with `Application.RegisterView`.
6
// E.g. "dirlist.html"
7
TmplName string
8
}
Copied!
Usage
1
options := iris.DirOptions{
2
// [...]
3
ShowList: true,
4
DirList: iris.DirListRich(),
5
}
Copied!

Example

Here's a full example that users can upload files and server can list them. It contains a customized listing page and delete file feature with basic authentication.
1
│ main.go # Iris web server.
2
└───views
3
│ upload.html # used to render upload form.
4
│ dirlist.html # used to render listing page.
5
└───uploads
6
│ # uploaded files will be stored here.
Copied!
Create a main.go file and copy-paste the following contents:
1
package main
2
3
import (
4
"crypto/md5"
5
"fmt"
6
"io"
7
"mime/multipart"
8
"os"
9
"path"
10
"strconv"
11
"strings"
12
"time"
13
14
"github.com/kataras/iris/v12"
15
"github.com/kataras/iris/v12/middleware/basicauth"
16
)
17
18
func init() {
19
os.Mkdir("./uploads", 0700)
20
}
21
22
const (
23
maxSize = 1 * iris.GB
24
uploadDir = "./uploads"
25
)
26
27
func main() {
28
app := iris.New()
29
30
view := iris.HTML("./views", ".html")
31
view.AddFunc("formatBytes", func(b int64) string {
32
const unit = 1000
33
if b < unit {
34
return fmt.Sprintf("%d B", b)
35
}
36
div, exp := int64(unit), 0
37
for n := b / unit; n >= unit; n /= unit {
38
div *= unit
39
exp++
40
}
41
return fmt.Sprintf("%.1f %cB",
42
float64(b)/float64(div), "kMGTPE"[exp])
43
})
44
app.RegisterView(view)
45
46
// Serve assets (e.g. javascript, css).
47
// app.HandleDir("/public", "./public")
48
49
app.Get("/", index)
50
51
app.Get("/upload", uploadView)
52
app.Post("/upload", upload)
53
54
filesRouter := app.Party("/files")
55
{
56
filesRouter.HandleDir("/", iris.Dir(uploadDir), iris.DirOptions{
57
Compress: true,
58
ShowList: true,
59
DirList: iris.DirListRich(iris.DirListRichOptions{
60
// Optionally, use a custom template for listing:
61
// Tmpl: dirListRichTemplate,
62
TmplName: "dirlist.html",
63
}),
64
})
65
66
auth := basicauth.Default(map[string]string{
67
"myusername": "mypassword",
68
})
69
70
filesRouter.Delete("/{file:path}", auth, deleteFile)
71
}
72
73
app.Listen(":8080")
74
}
75
76
func index(ctx iris.Context) {
77
ctx.Redirect("/upload")
78
}
79
80
func uploadView(ctx iris.Context) {
81
now := time.Now().Unix()
82
h := md5.New()
83
io.WriteString(h, strconv.FormatInt(now, 10))
84
token := fmt.Sprintf("%x", h.Sum(nil))
85
86
ctx.View("upload.html", token)
87
}
88
89
func upload(ctx iris.Context) {
90
ctx.SetMaxRequestBodySize(maxSize)
91
92
_, _, err := ctx.UploadFormFiles(uploadDir, beforeSave)
93
if err != nil {
94
ctx.StopWithError(iris.StatusPayloadTooRage, err)
95
return
96
}
97
98
ctx.Redirect("/files")
99
}
100
101
func beforeSave(ctx iris.Context, file *multipart.FileHeader) {
102
ip := ctx.RemoteAddr()
103
ip = strings.ReplaceAll(ip, ".", "_")
104
ip = strings.ReplaceAll(ip, ":", "_")
105
106
file.Filename = ip + "-" + file.Filename
107
}
108
109
func deleteFile(ctx iris.Context) {
110
// It does not contain the system path,
111
// as we are not exposing it to the user.
112
fileName := ctx.Params().Get("file")
113
114
filePath := path.Join(uploadDir, fileName)
115
116
if err := os.RemoveAll(filePath); err != nil {
117
ctx.StopWithError(iris.StatusInternalServerError, err)
118
return
119
}
120
121
ctx.Redirect("/files")
122
}
Copied!
The views/upload.html should look like that:
1
<!DOCTYPE html>
2
<html lang="en">
3
4
<head>
5
<meta charset="UTF-8">
6
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7
<title>Upload Files</title>
8
</head>
9
10
<body>
11
<form enctype="multipart/form-data" action="/upload" method="POST">
12
<input type="file" id="upload_files" name="upload_files" multiple />
13
<input type="hidden" name="token" value="{{.}}" />
14
15
<input type="button" value="Upload using JS" onclick="uploadFiles()" />
16
<input type="submit" value="Upload by submiting the form" />
17
</form>
18
19
<script type="text/javascript">
20
function uploadFiles() {
21
let files = document.getElementById("upload_files").files;
22
let formData = new FormData();
23
for (var i = 0; i < files.length; i++) {
24
formData.append("files[]", files[i]);
25
}
26
27
fetch('/upload',
28
{
29
method: "POST",
30
body: formData
31
}).
32
then(data => window.location = "/files").
33
catch(e => window.alert("upload failed: file too large"));
34
}
35
</script>
36
</body>
37
38
</html>
Copied!
And finally the customized listing page. Copy-paste the following code to the views/dirlist.html file:
1
<!DOCTYPE html>
2
<html lang="en">
3
4
<head>
5
<meta charset="UTF-8">
6
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7
<title>{{.Title}}</title>
8
<style>
9
a {
10
padding: 8px 8px;
11
text-decoration: none;
12
cursor: pointer;
13
color: #10a2ff;
14
}
15
16
table {
17
position: absolute;
18
top: 0;
19
bottom: 0;
20
left: 0;
21
right: 0;
22
height: 100%;
23
width: 100%;
24
border-collapse: collapse;
25
border-spacing: 0;
26
empty-cells: show;
27
border: 1px solid #cbcbcb;
28
}
29
30
table caption {
31
color: #000;
32
font: italic 85%/1 arial, sans-serif;
33
padding: 1em 0;
34
text-align: center;
35
}
36
37
table td,
38
table th {
39
border-left: 1px solid #cbcbcb;
40
border-width: 0 0 0 1px;
41
font-size: inherit;
42
margin: 0;
43
overflow: visible;
44
padding: 0.5em 1em;
45
}
46
47
table thead {
48
background-color: #10a2ff;
49
color: #fff;
50
text-align: left;
51
vertical-align: bottom;
52
}
53
54
table td {
55
background-color: transparent;
56
}
57
58
.table-odd td {
59
background-color: #f2f2f2;
60
}
61
62
.table-bordered td {
63
border-bottom: 1px solid #cbcbcb;
64
}
65
66
.table-bordered tbody>tr:last-child>td {
67
border-bottom-width: 0;
68
}
69
</style>
70
</head>
71
72
<body>
73
<table class="table-bordered table-odd">
74
<thead>
75
<tr>
76
<th>#</th>
77
<th>Name</th>
78
<th>Size</th>
79
<th>Actions</th>
80
</tr>
81
</thead>
82
<tbody>
83
{{ range $idx, $file := .Files }}
84
<tr>
85
<td>{{ $idx }}</td>
86
<td><a href="{{ $file.Path }}" title="{{ $file.ModTime }}">{{ $file.Name }}</a></td>
87
{{ if $file.Info.IsDir }}
88
<td>Dir</td>
89
{{ else }}
90
<td>{{ formatBytes $file.Info.Size }}</td>
91
{{ end }}
92
93
<td><input type="button" style="background-color:transparent;border:0px;cursor:pointer;" value=""
94
onclick="deleteFile({{ $file.RelPath }})" /></td>
95
</tr>
96
{{ end }}
97
</tbody>
98
</table>
99
<script type="text/javascript">
100
function deleteFile(filename) {
101
if (!confirm("Are you sure you want to delete " + filename + " ?")) {
102
return;
103
}
104
105
fetch('/files/' + filename,
106
{
107
method: "DELETE",
108
// If you don't want server to prompt for username/password:
109
// credentials:"include",
110
headers: {
111
// "Authorization": "Basic " + btoa("myusername:mypassword")
112
"X-Requested-With": "XMLHttpRequest",
113
},
114
}).
115
then(data => location.reload()).
116
catch(e => alert(e));
117
}
118
</script>
119
</body>
120
121
</html>
Copied!
Last modified 10mo ago
Export as PDF
Copy link
Contents
Example