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