• # Go

    Posté par . En réponse au message Service upload de fichiers http. Évalué à 1.

    En Go, en 1 seul fichier:

    package main
    import (
     "fmt"
     "io"
     "log"
     "net/http"
     "os"
     "path/filepath"
    )
    func main() {
     createUploadDir(uploadDir)
     // on enregistre le handler d'upload sur le / du site dans le routeur par défaut
     http.HandleFunc("/", handleUpload)
     // on écoute sur le port :9090 sur toutes les interfaces de la machine
     // en utilisant le routeur par défaut
     http.ListenAndServe(":9090", nil)
    }
    func handleUpload(w http.ResponseWriter, r *http.Request) {
     log.Println("method:", r.Method)
     if r.Method == "GET" {
     // On écrit dans le ResponseWriter le code HTML, ce qui permet d'afficher
     // la page sur le navigateur
     fmt.Fprint(w, uploadHTML)
     } else {
     // Le serveur utilisera 10Mo en mémoire pendant l'upload, le reste
     // sera stocker sur des fichiers temporaire sur le disque.
     r.ParseMultipartForm(10_000_000)
     file, header, err := r.FormFile("uploadfile")
     if err != nil {
     // On logge l'erreur sur le serveur
     log.Println(err)
     // On renvoit une erreur 500 avec le message d'erreur sur le navigateur
     http.Error(w, err.Error(), http.StatusInternalServerError)
     return
     }
     // On ferme le Reader des données de formulaire à la sortie de la fonction
     defer file.Close()
     // On ouvre un fichier en écriture pour enregistrer l'upload, dans le dossier d'upload
     f, err := os.OpenFile(filepath.Join(uploadDir, header.Filename), os.O_WRONLY|os.O_CREATE, 0666)
     if err != nil {
     log.Println(err)
     http.Error(w, err.Error(), http.StatusInternalServerError)
     return
     }
     // On ferme le fichier créé à la sortie de la fonction
     defer f.Close()
     // On copie les données d'upload dans le fichier sur le serveur
     _, err = io.Copy(f, file)
     if err != nil {
     log.Println(err)
     http.Error(w, err.Error(), http.StatusInternalServerError)
     return
     }
     // On écrite les métadonnées du fichier uploadé dans le ResponseWriter
     fmt.Fprint(w, header.Header)
     }
    }
    // createUploadDir créé un dossier d'upload où stocker les fichiers
    func createUploadDir(uploadDir string) {
     err := os.Mkdir(uploadDir, 0770)
     if err != nil {
     log.Println(err)
     }
    }
    const (
     // uploadDir est le dossier de stockage des fichiers uploadés
     uploadDir = "./test"
     // uploadHTML est une chaîne contenant le HTML de la page d'upload
     uploadHTML = `<html>
    <head>
     <title>Upload file</title>
    </head>
    <body>
    <form enctype="multipart/form-data" action="http://127.0.0.1:9090/" method="post">
     <input type="file" name="uploadfile" />
     <input type="submit" value="upload" />
    </form>
    </body>
    </html>`
    )

    Pas le code le plus idiomatique, ni le plus beau, mais il est concis et j'espère "facile" à comprendre. Si besoin d'explications, n'hésite pas.
    Mais clairement, il manque beaucoup pour en faire un truc vraiment correct (jeton CSRF, templating, CSS, etc).
    L'un des avantages, c'est que ça produit un simple fichier binaire que tu peux copier coller sur n'importe quel serveur en ssh, et ça tourne. Pas besoin d'installer 100 packages ou des dépendances dans tous les sens. Juste le binaire et c'est bon.

    C'est inspiré de https://astaxie.gitbooks.io/build-web-application-with-golang/en/04.5.html