put together the handling for photo uploaded page (untested)
This commit is contained in:
@@ -12,6 +12,7 @@ package ui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
@@ -34,6 +35,7 @@ type AmContext interface {
|
||||
FormField(string) string
|
||||
FormFieldInt(string) (int, error)
|
||||
FormFieldIsSet(string) bool
|
||||
FormFile(string) (*multipart.FileHeader, error)
|
||||
RC() int
|
||||
OutputType() string
|
||||
Parameter(string) string
|
||||
@@ -127,6 +129,11 @@ func (c *amContext) FormFieldIsSet(name string) bool {
|
||||
return req.Form.Has(name)
|
||||
}
|
||||
|
||||
// FormFile returns a "file" parameter from a multipart upload form.
|
||||
func (c *amContext) FormFile(name string) (*multipart.FileHeader, error) {
|
||||
return c.echoContext.FormFile(name)
|
||||
}
|
||||
|
||||
// RC returns the HTTP result code for the current operation.
|
||||
func (c *amContext) RC() int {
|
||||
return c.httprc
|
||||
|
||||
+64
-2
@@ -11,12 +11,22 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/gif"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.erbosoft.com/amy/amsterdam/database"
|
||||
"github.com/disintegration/imaging"
|
||||
)
|
||||
|
||||
//go:embed static_images/*
|
||||
@@ -43,17 +53,69 @@ func mimeTypeFromFilename(filename string) string {
|
||||
func AmServeImage(ctxt AmContext) (string, any, error) {
|
||||
components := strings.SplitAfter(ctxt.URLPath(), "/")
|
||||
var err error = nil
|
||||
var b []byte
|
||||
if len(components) == 4 {
|
||||
if components[2] == "builtin/" {
|
||||
switch components[2] {
|
||||
case "builtin/":
|
||||
var b []byte
|
||||
b, err = static_images.ReadFile(filepath.Join("static_images", components[3]))
|
||||
if err == nil {
|
||||
ctxt.SetOutputType(mimeTypeFromFilename(components[3]))
|
||||
return "bytes", b, nil
|
||||
}
|
||||
case "store/":
|
||||
var id int
|
||||
id, err = strconv.Atoi(components[3])
|
||||
if err == nil {
|
||||
var img *database.ImageStore
|
||||
img, err = database.AmLoadImage(int32(id))
|
||||
if err == nil {
|
||||
ctxt.SetOutputType(img.MimeType)
|
||||
return "bytes", img.Data, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ctxt.SetRC(http.StatusNotFound)
|
||||
// TODO: improve this error reporting
|
||||
return "string", fmt.Sprintf("File not found: %s", ctxt.URLPath()), err
|
||||
}
|
||||
|
||||
func AmProcessUploadedImage(fileheader *multipart.FileHeader, width, height int) ([]byte, string, error) {
|
||||
// open the file
|
||||
file, err := fileheader.Open()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// load the image from the file
|
||||
img, format, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// resize the image using high-quality Lanczos filter
|
||||
resized := imaging.Resize(img, width, height, imaging.Lanczos)
|
||||
|
||||
// re-encode it to the original format, or JPEG if that's not possible
|
||||
var buf bytes.Buffer
|
||||
var outType string
|
||||
switch strings.ToLower(format) {
|
||||
case "jpeg", "jpg":
|
||||
err = jpeg.Encode(&buf, resized, &jpeg.Options{Quality: 90})
|
||||
outType = "image/jpeg"
|
||||
case "gif":
|
||||
err = gif.Encode(&buf, resized, &gif.Options{NumColors: 256})
|
||||
outType = "image/gif"
|
||||
case "png":
|
||||
err = png.Encode(&buf, resized)
|
||||
outType = "image/png"
|
||||
default:
|
||||
err = jpeg.Encode(&buf, resized, &jpeg.Options{Quality: 90})
|
||||
outType = "image/jpeg"
|
||||
}
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return buf.Bytes(), outType, nil
|
||||
}
|
||||
|
||||
@@ -13,6 +13,20 @@
|
||||
<hr class="border-2 border-gray-400 w-4/5 mb-4">
|
||||
</div>
|
||||
|
||||
{{ if isset(errorMessage) }}
|
||||
<!-- Error Message Banner -->
|
||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-6" id="error-banner">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<span class="text-red-500 text-xl">⚠️</span>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm font-medium" id="error-message">{{ CapitalizeString(errorMessage) }}.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<!-- Upload Form -->
|
||||
<form method="POST" enctype="multipart/form-data" action="/profile_photo" class="max-w-2xl">
|
||||
<input type="hidden" name="tgt" value="{{ target }}">
|
||||
|
||||
Reference in New Issue
Block a user