--- 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.