---
name: file_save_disk
kind: function
lang: go
domain: infra
version: "1.0.0"
purity: impure
signature: "func FileSaveDisk(baseDir string, filename string, data io.Reader) (UploadedFile, error)"
description: "Escribe el contenido de un io.Reader a disco en baseDir con un nombre unico (UUID + extension). Crea el directorio si no existe. Retorna UploadedFile con metadata."
tags: [file, save, disk, storage, upload, infra]
uses_functions: [file_unique_name_go_infra]
uses_types: [UploadedFile_go_infra]
returns: [UploadedFile_go_infra]
returns_optional: false
error_type: "error_go_core"
imports: [fmt, io, mime, os, path/filepath, strings, time]
params:
  - name: baseDir
    desc: "directorio destino (se crea si no existe, permisos 0755)"
  - name: filename
    desc: "nombre original del archivo (solo se usa para extraer la extension)"
  - name: data
    desc: "reader con el contenido binario a escribir"
output: "UploadedFile con StoredName (UUID-based), Path completo, Size en bytes, ContentType inferido por extension y CreatedAt (UTC). Error si falla mkdir, create o copy"
tested: true
tests: ["guarda contenido en baseDir con nombre UUID", "crea baseDir si no existe", "tamano coincide con bytes escritos", "infiere ContentType desde la extension"]
test_file_path: "functions/infra/file_save_disk_test.go"
file_path: "functions/infra/file_save_disk.go"
---

## Ejemplo

```go
f, _ := os.Open("./input.png")
defer f.Close()
uploaded, err := FileSaveDisk("./uploads", "input.png", f)
if err != nil {
    log.Fatal(err)
}
fmt.Println(uploaded.Path) // ./uploads/{uuid}.png
```

## Notas

- El nombre original NUNCA se usa como nombre en disco (riesgo path traversal). Solo se preserva como metadata en el campo `Filename` para trazabilidad.
- ContentType se infiere de la extension via `mime.TypeByExtension`. Para validacion estricta del tipo real, llamar `FileValidateType` ANTES de guardar y/o sobreescribir el campo.
- Si falla el `io.Copy`, el archivo parcial se borra automaticamente.
