logo Buffalo slack logo
Carga de archivos
Guías

Carga de archivos#

Desde 0.10.3

Buffalo permite un manejo más fácil de archivos cargados desde un formulario. Almacenar estos archivos, tanto en el disco o S3, depende de ti, el desarrollador final: Buffalo solo te brinda el fácil acceso al archivo desde la solicitud.

Configuración del formulario#

El helper de formulario f.FileTag se puede usar para agregar rápidamente un archivo al formulario. Al usar esto, el enctype del formulario cambia automaticamente a multipart/form-data.

<%= form_for(widget, {action: widgetsPath(), method: "POST"}) { %>
  <%= f.InputTag("Name") %>
  <%= f.FileTag("MyFile", {name: "someFile"}) %>
  <button class="btn btn-success" role="submit">Save</button>
  <a href="<%= widgetsPath() %>" class="btn btn-warning" data-confirm="Are you sure?">Cancel</a>
<% } %>

Accediendo al archivo del formulario#

buffalo.Context tiene un método c.File que toma una cadena como parámetro, el name del input de archivo del formulario, y devolverá un binding.File que se puede usar para recuperar fácilmente un archivo desde este.

func SomeHandler(c buffalo.Context) error {
  // ...
  f, err := c.File("someFile")
  if err != nil {
    return errors.WithStack(err)
  }
  // ...
}

Mapeando a una estructura#

El c.Bind permite vincular elementos del formulario a una estructura, pero también puede adjuntar archivos cargados a la estructura. Para esto, el tipo del campo de la estructura DEBE ser de tipo binding.File

En el siguiente ejemplo podrás ver un modelo, el cual está configurado para tener un campo MyFile de tipo binding.File. Hay un callback AfterCreate en este modelo de ejemplo que guarda el archivo en el disco después que el modelo se haya guardado correctamente en la base de datos.

// models/widget.go
type Widget struct {
  ID        uuid.UUID    `json:"id" db:"id"`
  CreatedAt time.Time    `json:"created_at" db:"created_at"`
  UpdatedAt time.Time    `json:"updated_at" db:"updated_at"`
  Name      string       `json:"name" db:"name"`
  MyFile    binding.File `db:"-" form:"someFile"`
}

func (w *Widget) AfterCreate(tx *pop.Connection) error {
  if !w.MyFile.Valid() {
    return nil
  }
  dir := filepath.Join(".", "uploads")
  if err := os.MkdirAll(dir, 0755); err != nil {
    return errors.WithStack(err)
  }
  f, err := os.Create(filepath.Join(dir, w.MyFile.Filename))
  if err != nil {
    return errors.WithStack(err)
  }
  defer f.Close()
  _, err = io.Copy(f, w.MyFile)
  return err
}
El campo MyFile no se guarda en la base de datos debido al tag de estructura db:"-".

Probando la carga de archivos#

La librería de prueba de HTTP github.com/gobuffalo/httptest (incluida en el paquete github.com/gobuffalo/suite que usa Buffalo para pruebas) ha sido actualizado para incluir dos nuevas funciones: MultiPartPost y MultiPartPut.

Estos métodos funcionan como los métodos Post y Put, pero en su lugar, envía formularios multipart, y pueden aceptar archivos para cargar.

Al igual que Post y Put; MultiPartPost and MultiPartPut, toman una estructura o mapa como el primer argumento: Esto es el equivalente al formulario de HTML que se enviará. Los metodos toman un segundo armumento variable httptest.File.

Un httptest.File requiere el nombre del parámetro del formulario, ParanName; el nombre del archivo, FileName; y un io.Reader, presumiblemente el archivo que deseas cargar.

actions/widgets_test.go
actions/widgets.go
models/widgets.go
// actions/widgets_test.go

func (as *ActionSuite) Test_WidgetsResource_Create() {
  // clear out the uploads directory
  os.RemoveAll("./uploads")

  // setup a new Widget
  w := &models.Widget{Name: "Foo"}

  // find the file we want to upload
  r, err := os.Open("./logo.svg")
  as.NoError(err)
  // setup a new httptest.File to hold the file information
  f := httptest.File{
    // ParamName is the name of the form parameter
    ParamName: "someFile",
    // FileName is the name of the file being uploaded
    FileName: r.Name(),
    // Reader is the file that is to be uploaded, any io.Reader works
    Reader: r,
  }

  // Post the Widget and the File(s) to /widgets
  res, err := as.HTML("/widgets").MultiPartPost(w, f)
  as.NoError(err)
  as.Equal(302, res.Code)

  // assert the file exists on disk
  _, err = os.Stat("./uploads/logo.svg")
  as.NoError(err)

  // assert the Widget was saved to the DB correctly
  as.NoError(as.DB.First(w))
  as.Equal("Foo", w.Name)
  as.NotZero(w.ID)
}