Initial commit

This commit is contained in:
Kaustubh Maske Patil 2024-07-02 14:55:33 +05:30
commit 4f8fe007ad
2 changed files with 136 additions and 0 deletions

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module git.kaustubh.page/fun-with-files
go 1.22.4

133
main.go Normal file
View File

@ -0,0 +1,133 @@
package main
import (
"crypto/rand"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
)
var uploadDir string
func initConfig() {
if envUploadDir := os.Getenv("UPLOAD_DIR"); envUploadDir != "" {
uploadDir = envUploadDir
} else {
uploadDir = "./uploads/"
}
}
func main() {
initConfig()
if len(os.Args) < 2 {
fmt.Println("Usage: go run main.go <port>")
return
}
port := os.Args[1]
uploadEndpoint := "upload"
if len(os.Args) >= 3 {
uploadEndpoint = os.Args[2]
}
http.HandleFunc(fmt.Sprintf("/%s/", uploadEndpoint), uploadHandler)
fmt.Printf("Server started at :%s\n", port)
fmt.Printf("Endpoint: /%s/\n", uploadEndpoint)
fmt.Printf("Upload Directory: %s\n", uploadDir)
http.ListenAndServe(":"+port, nil)
}
func uploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
return
}
// Parse the multipart form
err := r.ParseMultipartForm(10 << 20) // 10 MB
if err != nil {
http.Error(w, "Error parsing form data", http.StatusBadRequest)
return
}
file, handler, err := r.FormFile("file")
if err != nil {
http.Error(w, "Error retrieving the file", http.StatusBadRequest)
return
}
defer file.Close()
// Sanitize the filename
sanitizedFilename := sanitizeFilename(handler.Filename)
if sanitizedFilename == "" {
sanitizedFilename = generateRandomFilename()
}
filePath := filepath.Join(uploadDir, sanitizedFilename)
// Ensure unique filename
for fileExists(filePath) {
sanitizedFilename = generateRandomFilename()
filePath = filepath.Join(uploadDir, sanitizedFilename)
}
// Create the uploads directory if it doesn't exist
if _, err := os.Stat(uploadDir); os.IsNotExist(err) {
os.MkdirAll(uploadDir, os.ModePerm)
}
// Save the file
out, err := os.Create(filePath)
if err != nil {
http.Error(w, "Unable to save the file", http.StatusInternalServerError)
return
}
defer out.Close()
_, err = io.Copy(out, file)
if err != nil {
http.Error(w, "Error saving the file", http.StatusInternalServerError)
return
}
w.Write([]byte("File uploaded successfully"))
}
func sanitizeFilename(filename string) string {
// Allow only alphanumeric, dashes, underscores, and dots
re := regexp.MustCompile(`[^a-zA-Z0-9._-]`)
sanitized := re.ReplaceAllString(filename, "")
sanitized = strings.TrimLeft(sanitized, ".")
return sanitized
}
func generateRandomFilename() string {
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, 16)
for i := range b {
b[i] = letters[randInt(len(letters))]
}
return string(b) + ".file"
}
func randInt(n int) int {
b := make([]byte, 1)
rand.Read(b)
return int(b[0]) % n
}
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}