fupie/ws/upload.go

79 lines
1.8 KiB
Go

package ws
import (
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"sectorinf.com/emilis/fupie/ws/route"
)
var (
base64Stripper = strings.NewReplacer("/", "", "+", "", "=", "")
)
func Upload(rootDir, redirect string, maxBytes int64) (route.HandlerFunc, error) {
stat, err := os.Stat(rootDir)
if err != nil {
return nil, fmt.Errorf("stat [%s]: %w", rootDir, err)
}
if !stat.IsDir() {
return nil, fmt.Errorf("root path [%s] is not a directory", rootDir)
}
rootDir, err = filepath.Abs(rootDir)
if err != nil {
return nil, fmt.Errorf("abs filepath [%s]: %w", rootDir, err)
}
redirect = strings.TrimRight(redirect, "/")
return func(ctx route.Context) {
if err := ctx.Request.ParseMultipartForm(maxBytes); err != nil {
ctx.Status(http.StatusBadRequest)
return
}
uploads := ctx.Request.MultipartForm.File["upload"]
if len(uploads) != 1 {
ctx.Status(http.StatusBadRequest)
return
}
header := uploads[0]
file, err := header.Open()
if err != nil {
ctx.InternalServerErrorf("header.Open: %s", err.Error())
return
}
newFilename := rename(header.Filename)
path := filepath.Join(rootDir, newFilename)
fsFile, err := os.Create(path)
if err != nil {
ctx.InternalServerErrorf("creating file at [%s]: %s", path, err.Error())
return
}
if _, err := io.CopyBuffer(fsFile, file, nil); err != nil {
ctx.InternalServerErrorf("writing file at [%s]: %s", path, err.Error())
return
}
fsFile.Close()
ctx.SeeOther(fmt.Sprintf("%s/%s", redirect, newFilename))
}, nil
}
func rename(name string) string {
return fmt.Sprintf("%s_%s", randName(4), name)
}
func randName(bytes int) string {
buffer := make([]byte, bytes)
_, err := rand.Read(buffer)
if err != nil {
panic(err)
}
return base64Stripper.Replace(base64.StdEncoding.EncodeToString(buffer))
}