package main
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"golang.org/x/sync/errgroup"
)
const (
maxUploadSize = 1024 * 1024 * 5
maxImageCount = 5
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write(indexHTML)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
})
http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize)
err := r.ParseMultipartForm(1024 * 32)
if err != nil {
fmt.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
imageCount, err := strconv.Atoi(r.FormValue("imageCount"))
if err != nil {
fmt.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
if imageCount > maxImageCount {
fmt.Printf("too many images %d\n", imageCount)
w.WriteHeader(http.StatusBadRequest)
return
}
eg := errgroup.Group{}
for i := 0; i < imageCount; i++ {
key := fmt.Sprintf("image%d", i)
eg.Go(func() error {
imgF, fh, err := r.FormFile(key)
if err != nil {
return err
}
defer imgF.Close()
// adjust this to fit your accepted content types
ct := fh.Header.Get("Content-Type")
parts := strings.Split(ct, "/")
if len(parts) < 2 || parts[0] != "image" {
return fmt.Errorf("invalid content type %s for %s", ct, key)
}
ext := parts[len(parts)-1]
f, err := os.Create(fmt.Sprintf("%s.%s", key, ext))
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, imgF)
return err
})
}
err = eg.Wait()
if err != nil {
fmt.Println(err)
// TODO: better distinguish bad request and internal server error
w.WriteHeader(http.StatusBadRequest)
return
}
})
http.ListenAndServe(":4321", nil)
}
var indexHTML = []byte(`
<body>
<h1>image upload</h1>
<input type="file" multiple accept="image/*" onchange="handleImageUpload(event);" />
<script>
async function handleImageUpload(event) {
const options = {
maxSizeMB: .5,
useWebWorker: true,
};
console.time("time")
try {
const files = Array.from(event.target.files);
const formData = new FormData();
formData.append("imageCount", files.length);
console.time("compress")
const compressedFiles = await Promise.all(files.map(f => imageCompression(f, options)))
console.timeEnd("compress")
compressedFiles.forEach((cf, idx) => formData.append(` + "`image${idx}`" + `, cf))
await fetch("/upload", {
method: "POST",
body: formData,
})
} catch (error) {
console.log(error);
}
console.timeEnd("time")
}
</script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/browser-image-compression.js"></script>
</body>
`)
Created
August 21, 2023 00:41
-
-
Save dillonstreator/2075fcc633225ad1172135ee1ca33cb4 to your computer and use it in GitHub Desktop.
Golang http server arbitrary image count upload with `browser-image-compression` and concurrent streaming
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment