J'essaie de télécharger des images de mon ordinateur sur un site Web à l'aide de go. D'habitude, j'utilise un script bash qui envoie un fichier et une clé au serveur:
curl -F "image"=@"IMAGEFILE" -F "key"="KEY" URL
cela fonctionne bien, mais j'essaie de convertir cette demande en mon programme golang.
http://matt.aimonetti.net/posts/2013/07/01/golang-multipart-file-upload-example/
J'ai essayé ce lien et bien d'autres, mais pour chaque code que j'ai essayé, la réponse du serveur est "aucune image envoyée", et je ne sais pas pourquoi. Si quelqu'un sait ce qui se passe avec l'exemple ci-dessus.
Voici un exemple de code.
En bref, vous devrez utiliser le mime/multipart
package pour construire le formulaire.
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/http/httptest"
"net/http/httputil"
"os"
"strings"
)
func main() {
var client *http.Client
var remoteURL string
{
//setup a mocked http client.
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
b, err := httputil.DumpRequest(r, true)
if err != nil {
panic(err)
}
fmt.Printf("%s", b)
}))
defer ts.Close()
client = ts.Client()
remoteURL = ts.URL
}
//prepare the reader instances to encode
values := map[string]io.Reader{
"file": mustOpen("main.go"), // lets assume its this file
"other": strings.NewReader("hello world!"),
}
err := Upload(client, remoteURL, values)
if err != nil {
panic(err)
}
}
func Upload(client *http.Client, url string, values map[string]io.Reader) (err error) {
// Prepare a form that you will submit to that URL.
var b bytes.Buffer
w := multipart.NewWriter(&b)
for key, r := range values {
var fw io.Writer
if x, ok := r.(io.Closer); ok {
defer x.Close()
}
// Add an image file
if x, ok := r.(*os.File); ok {
if fw, err = w.CreateFormFile(key, x.Name()); err != nil {
return
}
} else {
// Add other fields
if fw, err = w.CreateFormField(key); err != nil {
return
}
}
if _, err = io.Copy(fw, r); err != nil {
return err
}
}
// Don't forget to close the multipart writer.
// If you don't close it, your request will be missing the terminating boundary.
w.Close()
// Now that you have a form, you can submit it to your handler.
req, err := http.NewRequest("POST", url, &b)
if err != nil {
return
}
// Don't forget to set the content type, this will contain the boundary.
req.Header.Set("Content-Type", w.FormDataContentType())
// Submit the request
res, err := client.Do(req)
if err != nil {
return
}
// Check the response
if res.StatusCode != http.StatusOK {
err = fmt.Errorf("bad status: %s", res.Status)
}
return
}
func mustOpen(f string) *os.File {
r, err := os.Open(f)
if err != nil {
panic(err)
}
return r
}
Après avoir dû décoder la réponse acceptée pour cette question afin de l'utiliser dans mes tests unitaires, j'ai finalement obtenu le code refactoré suivant:
func createMultipartFormData(t *testing.T, fieldName, fileName string) (bytes.Buffer, *multipart.Writer) {
var b bytes.Buffer
var err error
w := multipart.NewWriter(&b)
var fw io.Writer
file := mustOpen(fileName)
if fw, err = w.CreateFormFile(fieldName, file.Name()); err != nil {
t.Errorf("Error creating writer: %v", err)
}
if _, err = io.Copy(fw, file); err != nil {
t.Errorf("Error with io.Copy: %v", err)
}
w.Close()
return b, w
}
func mustOpen(f string) *os.File {
r, err := os.Open(f)
if err != nil {
pwd, _ := os.Getwd()
fmt.Println("PWD: ", pwd)
panic(err)
}
return r
}
Maintenant, il devrait être assez facile à utiliser:
b, w := createMultipartFormData(t, "image","../luke.png")
req, err := http.NewRequest("POST", url, &b)
if err != nil {
return
}
// Don't forget to set the content type, this will contain the boundary.
req.Header.Set("Content-Type", w.FormDataContentType())
En ce qui concerne la publication d'attila-o, l'en-tête de la requête n'a pas de limite puisque l'enregistreur est déjà fermé.
// after the close, the bounday will be nil.
w.Close()
...
req.Header.Set("Content-Type", w.FormDataContentType())
Donc, il devrait fermer après le set, je pense.
req.Header.Set("Content-Type", w.FormDataContentType())
w.Close()
Cela pourrait être utile https://github.com/030/go-curl
./go-curl -url \ http://localhost:9999/service/rest/v1/components?repository=maven-releases \ -user admin -pass admin123 -F \ "maven2.asset1=@utils/test-files-multipart/file1.pom,\ maven2.asset1.extension=pom,\ maven2.asset2=@utils/test-files-multipart/file1.jar,\ maven2.asset2.extension=jar,\ maven2.asset3=@utils/test-files-multipart/file1-sources.jar,\ maven2.asset3.extension=sources.jar"
J'ai trouvé ce tutoriel très utile pour clarifier mes confusions sur le téléchargement de fichiers dans Go.
Fondamentalement, vous téléchargez le fichier via ajax en utilisant form-data
sur un client et utilisez le petit extrait de code Go suivant sur le serveur:
file, handler, err := r.FormFile("img") // img is the key of the form-data
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
fmt.Println("File is good")
fmt.Println(handler.Filename)
fmt.Println()
fmt.Println(handler.Header)
f, err := os.OpenFile(handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
Ici r
est *http.Request
. P.S. cela stocke simplement le fichier dans le même dossier et n'effectue aucune vérification de sécurité.