Skip to content

Instantly share code, notes, and snippets.

@ezr
Last active May 23, 2023 04:25
Show Gist options
  • Save ezr/392fc583346712eb5bb90b8ee5f38227 to your computer and use it in GitHub Desktop.
Save ezr/392fc583346712eb5bb90b8ee5f38227 to your computer and use it in GitHub Desktop.
HTTP server to accept file uploads
package main
/* Taken from https://github.com/TannerGabriel/learning-go/tree/master/beginner-programs/FileUpload with some tweaks
*/
import (
"flag"
"fmt"
"html/template"
"io"
"net"
"net/http"
"os"
"strings"
)
// Compile templates on start of the application
var tmpl, _ = template.New("upload").Parse(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Upload File</title>
<style>
body {
background-color: #3b3b3b;
color: #eeeeee;
font-size: 1.5em;
font-family: sans-serif;
}
</style>
</head>
<body>
<form
enctype="multipart/form-data"
action="http://{{.Host}}/"
method="post"
>
<input type="file" name="myFile" />
<input type="submit" value="upload" />
</form>
<p>{{.Message}}<i>{{.Filename}}</i></p>
<br>
<p>curl upload: <code>curl -F myFile=@FileName http://{{.Host}}/</code></p>
</body>
</html>`)
type pageData struct {
Host string
Message string
Filename string
}
func uploadFile(w http.ResponseWriter, r *http.Request) {
// Maximum upload of 10 MB files
r.ParseMultipartForm(10 << 20)
// Get handler for filename, size and headers
file, handler, err := r.FormFile("myFile")
if err != nil {
fmt.Println("Error Retrieving the File")
fmt.Println(err)
display(w, r, "Error retrieving the file. Upload failed.", "")
return
}
defer file.Close()
fmt.Printf("Uploaded File: %+v\n", handler.Filename)
fmt.Printf("File Size: %+v\n", byteCountIEC(handler.Size))
fmt.Printf("MIME Header: %+v\n", handler.Header)
// Create file
dst, err := os.Create(handler.Filename)
defer dst.Close()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Copy the uploaded file to the created file on the filesystem
if _, err := io.Copy(dst, file); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
display(w, r, "Successfully Uploaded File ", handler.Filename)
}
func display(w http.ResponseWriter, r *http.Request, m, fname string) {
page := pageData{
Host: r.Host,
Message: m,
Filename: fname,
}
tmpl.Execute(w, page)
}
func uploadHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
display(w, r, "", "")
case "POST":
uploadFile(w, r)
}
}
// function from https://yourbasic.org/golang/byte-count.go
func byteCountIEC(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])
}
func printIPAddrs() {
ifaces, err := net.Interfaces()
if err != nil {
// this function isn't critical, let's just give up
return
}
for _, intf := range ifaces {
addrs, err := intf.Addrs()
if err != nil {
return
}
if strings.Contains(intf.Flags.String(), "up") {
for _, addr := range addrs {
fmt.Printf(" %s\n", strings.Split(addr.String(), "/")[0])
}
}
}
}
func main() {
port := flag.String("port", "9090", "port to serve on")
addr := flag.String("addr", "", "address to serve from (defaults to all interfaces)")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
flag.PrintDefaults()
fmt.Fprintf(os.Stderr, "\nUploading file foo with curl:\n")
fmt.Fprintf(os.Stderr, " $ curl -F myFile=@foo http://<ip>:<port>/\n")
}
flag.Parse()
http.HandleFunc("/", uploadHandler)
listenSocket := fmt.Sprintf("%s:%s", *addr, *port)
fmt.Println("[*] listening on", listenSocket)
if *addr == "" {
fmt.Println("[*] current addresses:")
printIPAddrs()
}
http.ListenAndServe(listenSocket, nil)
}
@ezr
Copy link
Author

ezr commented Aug 13, 2020

Upload a file with curl:
curl -F myFile=@FileName http://127.0.0.1:9090/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment