commit
This commit is contained in:
		
							
								
								
									
										158
									
								
								handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								handler.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,158 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"html/template" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 	"os" | ||||||
|  | 	"path" | ||||||
|  | 	"sort" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func renderError(w http.ResponseWriter, msg string, code int) { | ||||||
|  | 	http.Error(w, msg, 404) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func byteCountBinary(b int64) string { | ||||||
|  | 	const unit = 1024 | ||||||
|  | 	if b < unit { | ||||||
|  | 		return fmt.Sprintf("%d B", b) | ||||||
|  | 	} | ||||||
|  | 	div, exp := int64(unit), 0 | ||||||
|  | 	for n := b / unit; n >= unit; n /= unit { | ||||||
|  | 		div *= unit | ||||||
|  | 		exp++ | ||||||
|  | 	} | ||||||
|  | 	return fmt.Sprintf("%.1f %ciB", float64(b)/float64(div), "KMGTPE"[exp]) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var htmlReplacer = strings.NewReplacer( | ||||||
|  | 	"&", "&", | ||||||
|  | 	"<", "<", | ||||||
|  | 	">", ">", | ||||||
|  | 	// """ is shorter than """. | ||||||
|  | 	`"`, """, | ||||||
|  | 	// "'" is shorter than "'" and apos was not in HTML until HTML5. | ||||||
|  | 	"'", "'", | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func localRedirect(w http.ResponseWriter, r *http.Request, newPath string) { | ||||||
|  | 	if q := r.URL.RawQuery; q != "" { | ||||||
|  | 		newPath += "?" + q | ||||||
|  | 	} | ||||||
|  | 	w.Header().Set("Location", newPath) | ||||||
|  | 	w.WriteHeader(http.StatusMovedPermanently) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func toHTTPError(err error) (msg string, httpStatus int) { | ||||||
|  | 	if os.IsNotExist(err) { | ||||||
|  | 		return "404 page not found", http.StatusNotFound | ||||||
|  | 	} | ||||||
|  | 	if os.IsPermission(err) { | ||||||
|  | 		return "403 Forbidden", http.StatusForbidden | ||||||
|  | 	} | ||||||
|  | 	// Default: | ||||||
|  | 	return "500 Internal Server Error", http.StatusInternalServerError | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func serveFile(w http.ResponseWriter, r *http.Request, fs http.FileSystem, name string) { | ||||||
|  |  | ||||||
|  | 	f, err := fs.Open(name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		msg, code := toHTTPError(err) | ||||||
|  | 		renderError(w, msg, code) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	defer f.Close() | ||||||
|  |  | ||||||
|  | 	d, err := f.Stat() | ||||||
|  | 	if err != nil { | ||||||
|  | 		msg, code := toHTTPError(err) | ||||||
|  | 		renderError(w, msg, code) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// redirect if the directory name doesn't end in a slash | ||||||
|  | 	if d.IsDir() { | ||||||
|  | 		url := r.URL.Path | ||||||
|  | 		if url[len(url)-1] != '/' { | ||||||
|  | 			localRedirect(w, r, path.Base(url)+"/") | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Still a directory? (we didn't find an index.html file) | ||||||
|  | 	if d.IsDir() { | ||||||
|  | 		dirList(w, r, f, name) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// serveContent will check modification time | ||||||
|  | 	// sizeFunc := func() (int64, error) { return d.Size(), nil } | ||||||
|  | 	http.ServeContent(w, r, d.Name(), d.ModTime(), f) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func dirList(w http.ResponseWriter, r *http.Request, f http.File, name string) { | ||||||
|  | 	dirs, err := f.Readdir(-1) | ||||||
|  | 	if err != nil { | ||||||
|  | 		renderError(w, "Error reading directory", http.StatusInternalServerError) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() }) | ||||||
|  |  | ||||||
|  | 	w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||||||
|  |  | ||||||
|  | 	t, err := template.New("main.html").ParseFiles("main.html") | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Printf(err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	data := &Data{ | ||||||
|  | 		Title: name, | ||||||
|  | 		Files: make([]*File, 0, len(dirs)), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, d := range dirs { | ||||||
|  | 		name := d.Name() | ||||||
|  | 		icon := "file" | ||||||
|  | 		size := byteCountBinary(d.Size()) | ||||||
|  | 		if d.IsDir() { | ||||||
|  | 			name += "/" | ||||||
|  | 			icon = "folder" | ||||||
|  | 			size = "-" | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		url := url.URL{Path: name} | ||||||
|  |  | ||||||
|  | 		data.Files = append(data.Files, &File{ | ||||||
|  | 			Icon: icon, | ||||||
|  | 			URL:  url.String(), | ||||||
|  | 			Name: htmlReplacer.Replace(name), | ||||||
|  | 			Size: size, | ||||||
|  | 			Date: d.ModTime().Format("02.01.2006 15:04:05"), | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = t.Execute(w, data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Printf(err.Error()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type fileHandler struct { | ||||||
|  | 	root string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (fh *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	upath := r.URL.Path | ||||||
|  | 	if !strings.HasPrefix(upath, "/") { | ||||||
|  | 		upath = "/" + upath | ||||||
|  | 		r.URL.Path = upath | ||||||
|  | 	} | ||||||
|  | 	serveFile(w, r, http.Dir(fh.root), path.Clean(upath)) | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"flag" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var port uint | ||||||
|  | var folderPath string | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	flag.StringVar(&folderPath, "p", "", "Path to folder (required)") | ||||||
|  | 	flag.UintVar(&port, "l", 8080, "Listening port (default 8080)") | ||||||
|  | 	flag.Parse() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  |  | ||||||
|  | 	if folderPath == "" { | ||||||
|  | 		fmt.Fprintf(os.Stderr, "Error: parametr 'path' is required") | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	server := &http.Server{ | ||||||
|  | 		Addr: fmt.Sprintf(":%d", port), | ||||||
|  | 		Handler: &fileHandler{ | ||||||
|  | 			root: folderPath, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	server.ListenAndServe() | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								main.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								main.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  | <head> | ||||||
|  |     <meta charset="utf-8" /> | ||||||
|  |     <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||||
|  |     <title>{{.Title}}</title> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
|  |  | ||||||
|  |     <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/regular.css" integrity="sha384-ZlNfXjxAqKFWCwMwQFGhmMh3i89dWDnaFU2/VZg9CvsMGA7hXHQsPIqS+JIAmgEq" crossorigin="anonymous">    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/fontawesome.css" integrity="sha384-1rquJLNOM3ijoueaaeS5m+McXPJCGdr5HcA03/VHXxcp2kX2sUrQDmFc3jR5i/C7" crossorigin="anonymous"> | ||||||
|  |  | ||||||
|  | 	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"> | ||||||
|  |     <style> | ||||||
|  |         body { | ||||||
|  |             padding: 20px; | ||||||
|  |         } | ||||||
|  |     </style> | ||||||
|  | </head> | ||||||
|  | <body>   | ||||||
|  |         <div class="container"> | ||||||
|  |             <div class="alert alert-primary" role="alert"> | ||||||
|  |                 <b>{{.Title}}</b> | ||||||
|  |             </div> | ||||||
|  |             <br> | ||||||
|  |             <table class="table"> | ||||||
|  |                 <tr> | ||||||
|  |                     <th> </th> | ||||||
|  |                     <th>Name</th>  | ||||||
|  |                     <th>Size</th> | ||||||
|  |                     <th>Date</th> | ||||||
|  |                 </tr> | ||||||
|  |  | ||||||
|  |                 {{range .Files}} | ||||||
|  |                 <tr> | ||||||
|  |                     <td><i class="far fa-{{.Icon}}"></i></td> | ||||||
|  |                     <td><a href="{{.URL}}">{{.Name}}</a></td>  | ||||||
|  |                     <td>{{.Size}}</td> | ||||||
|  |                     <td>{{.Date}}</td> | ||||||
|  |                 </tr> | ||||||
|  |                 {{end}} | ||||||
|  |             </table> | ||||||
|  |         </div> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
|  |  | ||||||
		Reference in New Issue
	
	Block a user