Supón que tienes un sitio web como Instagram y quieres que tus usuarios suban sus fotos. ¿Cómo implementarías esa funcionalidad?
Tienes que agregar la propiedad enctype
al formulario que vas a usar para subir fotos. Aquí están los tres posibles valores para esta propiedad:
application/x-www-form-urlencoded. Transforma todos los caracteres antes de subirlos (Por defecto).
multipart/form-data Sin transformación. Debes usar esta valor cuando vas a subir fotos.
text/plain Convierte los espacios en "+" sin transformar los caracteres.
Entonces el contenido HTML para subir un archivo debería verse como esto:
<html>
<head>
<title>Upload file</title>
</head>
<body>
<form enctype="multipart/form-data" action="http://127.0.0.1:9090/upload" method="post">
<input type="file" name="uploadfile" />
<input type="hidden" name="token" value="{{.}}"/>
<input type="submit" value="Subit" />
</form>
</body>
</html>
Necesitamos añadir la funcionalidad al servidor para manejar este formulario.
http.HandleFunc("/upload", upload)
// lógica de subida
func upload(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method)
if r.Method == "GET" {
crutime := time.Now().Unix()
h := md5.New()
io.WriteString(h, strconv.FormatInt(crutime, 10))
token := fmt.Sprintf("%x", h.Sum(nil))
t, _ := template.ParseFiles("upload.gtpl")
t.Execute(w, token)
} else {
r.ParseMultipartForm(32 << 20)
file, handler, err := r.FormFile("uploadfile")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
fmt.Fprintf(w, "%v", handler.Header)
f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
}
}
Como puedes ver, llamamo a r.ParseMultipartForm
para subir archivos. La función ParseMultipartForm toma el argumento maxMemory
. Después de llamar ParseMultipartForm
, el archivo puede ser guardado en el servidor con tamaño maxMemory
. Si el tamaño del archivo es mayor que maxMemory
, el resto del archivo será guardado en un archivo temporal del sistema. Puedes usar r.FormFile
para obtener el archivo y usar io.Copy
para guardarlo en tu sistema.
Puede que no necesites llamar a r.ParseForm
cuando acceses a otros campos, porque Go llamará a este método si es necesario. También llamar a ParseMultipartForm
una vez es suficiente, múltiples llamadas no hacen diferencia.
Usamos tres pasos para subir archivos:
- Agregar
enctype="multipart/form-data"
a tu formulario - Llamar
r.ParseMultipartForm
en el lado del servidor para guardar el archivo en memoria o en un archivo temporal. - Llamar
r.FormFile
para obtener el archivo y guardarlo en el sistema de ficheros.
El manejador del archivo es multipart.FileHeader
. Este usa la siguiente estructura:
type FileHeader struct {
Filename string
Header textproto.MIMEHeader
// contains filtered or unexported fields
}
Figure 4.5 Imprimir la información del servidor después de recibir el archivo.
Les mostré en el ejemplo anterior como usar un formulario para subir un archivo. Nosotros podemos usar un cliente en Go para emular la subida de archivos.
package main
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"os"
)
func postFile(filename string, targetUrl string) error {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
// este paso es muy importante
fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename)
if err != nil {
fmt.Println("error writing to buffer")
return err
}
// Abrir el archivo para manejarlo
fh, err := os.Open(filename)
if err != nil {
fmt.Println("error opening file")
return err
}
//iocopy
_, err = io.Copy(fileWriter, fh)
if err != nil {
return err
}
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
resp, err := http.Post(targetUrl, contentType, bodyBuf)
if err != nil {
return err
}
defer resp.Body.Close()
resp_body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
fmt.Println(resp.Status)
fmt.Println(string(resp_body))
return nil
}
// Ejemplo de uso
func main() {
target_url := "http://localhost:9090/upload"
filename := "./astaxie.pdf"
postFile(filename, target_url)
}
El código de arriba muestra como usar un cliente para subir archivos. El usa multipart.Write
para escribir archivos en un cache y enviarlos al servidor a través de un método POST.
Si tienes otros campos que necesitas escribir en el cuerpo del mensaje, como un nombre d eusuario, llama a multipart.WriteField
cada que lo necesites.
- Índice
- Sección anterior: Envíos duplicados
- Siguiente sección: Resumen