feat: file_save_disk, file_delete, file_serve, upload_parse, upload_handler, thumbnail_generate (issue 0014 fase 3)

This commit is contained in:
2026-04-18 17:15:39 +02:00
parent 1675d2bb84
commit 4b420fb24b
18 changed files with 1114 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
package infra
import (
"fmt"
"io"
"mime"
"os"
"path/filepath"
"strings"
"time"
)
// FileSaveDisk escribe el contenido de data en baseDir con un nombre unico generado a
// partir de filename original. Crea baseDir si no existe.
//
// Retorna el UploadedFile con la metadata y la ruta completa en disco. El campo
// ContentType se infiere de la extension via mime.TypeByExtension; si la app necesita
// validacion mas estricta, debe usar FileValidateType antes y/o sobreescribir el campo.
func FileSaveDisk(baseDir string, filename string, data io.Reader) (UploadedFile, error) {
if err := os.MkdirAll(baseDir, 0o755); err != nil {
return UploadedFile{}, fmt.Errorf("file_save_disk: mkdir %s: %w", baseDir, err)
}
stored := FileUniqueName(filename)
dst := filepath.Join(baseDir, stored)
f, err := os.Create(dst)
if err != nil {
return UploadedFile{}, fmt.Errorf("file_save_disk: create %s: %w", dst, err)
}
defer f.Close()
n, err := io.Copy(f, data)
if err != nil {
_ = os.Remove(dst)
return UploadedFile{}, fmt.Errorf("file_save_disk: copy: %w", err)
}
ext := strings.ToLower(filepath.Ext(stored))
ct := mime.TypeByExtension(ext)
if ct == "" {
ct = "application/octet-stream"
}
return UploadedFile{
Filename: filename,
StoredName: stored,
Size: n,
ContentType: ct,
Path: dst,
CreatedAt: time.Now().UTC(),
}, nil
}