Content-Type multipart / form-data를 사용하는 POST 데이터
go를 사용하여 컴퓨터에서 웹 사이트로 이미지를 업로드하려고합니다. 일반적으로 파일과 키를 서버에 보내는 bash 스크립트를 사용합니다.
curl -F "image"=@"IMAGEFILE" -F "key"="KEY" URL
잘 작동하지만이 요청을 내 golang 프로그램으로 변환하려고합니다.
http://matt.aimonetti.net/posts/2013/07/01/golang-multipart-file-upload-example/
이 링크와 다른 많은 링크를 시도했지만 시도한 각 코드에 대해 서버의 응답은 "전송 된 이미지 없음"이며 이유를 모르겠습니다. 누군가가 위의 예에서 무슨 일이 일어나고 있는지 알고 있다면.
다음은 몇 가지 샘플 코드입니다.
요컨대, 양식을 빌드하려면 mime/multipart
패키지 를 사용해야합니다 .
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
}
attila-o의 게시물과 관련하여 Writer가 이미 닫혀 있으므로 요청 헤더에 경계가 없습니다.
// after the close, the bounday will be nil.
w.Close()
...
req.Header.Set("Content-Type", w.FormDataContentType())
그래서 세트가 끝나면 닫혀 야한다고 생각합니다.
req.Header.Set("Content-Type", w.FormDataContentType())
w.Close()
내 단위 테스트에서 사용하기 위해이 질문에 대해 허용 된 답변을 디코딩해야했던 후에 마침내 다음과 같은 리팩터링 된 코드를 얻게되었습니다.
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
}
이제 사용하기가 매우 쉽습니다.
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())
다음 io.Pipe()
은 전체 파일을 메모리로 읽거나 버퍼를 관리 할 필요가 없도록 사용하는 데 사용한 함수 입니다. 단일 파일 만 처리하지만 고 루틴 내에 더 많은 부분을 추가하여 더 많은 것을 처리하도록 쉽게 확장 할 수 있습니다. 행복한 길은 잘 작동합니다. 오류 경로는 많은 테스트를 수행하지 않았습니다.
import (
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func UploadMultipartFile(client *http.Client, uri, key, path string) (*http.Response, error) {
body, writer := io.Pipe()
req, err := http.NewRequest(http.MethodPost, uri, body)
if err != nil {
return nil, err
}
mwriter := multipart.NewWriter(writer)
req.Header.Add("Content-Type", mwriter.FormDataContentType())
errchan := make(chan error)
go func() {
defer close(errchan)
defer writer.Close()
defer mwriter.Close()
w, err := mwriter.CreateFormFile(key, path)
if err != nil {
errchan <- err
return
}
in, err := os.Open(path)
if err != nil {
errchan <- err
return
}
defer in.Close()
if written, err := io.Copy(w, in); err != nil {
errchan <- fmt.Errorf("error copying %s (%d bytes written): %v", path, written, err)
return
}
if err := mwriter.Close(); err != nil {
errchan <- err
return
}
}()
resp, err := client.Do(req)
merr := <-errchan
if err != nil || merr != nil {
return resp, fmt.Errorf("http error: %v, multipart error: %v", err, merr)
}
return resp, nil
}
이 튜토리얼 은 Go에서 파일 업로드에 대한 혼란을 명확히하는 데 매우 유용 하다는 것을 알았습니다 .
기본적으로 form-data
클라이언트에서 사용 하여 ajax를 통해 파일을 업로드 하고 서버에서 다음 작은 Go 코드 스 니펫을 사용합니다.
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)
Here r
is *http.Request
. P.S. this just stores the file in the same folder and does not perform any security checks.
참고URL : https://stackoverflow.com/questions/20205796/post-data-using-the-content-type-multipart-form-data
'Programing' 카테고리의 다른 글
ARC 아래의 NSString 속성은 강력해야합니까 아니면 복사해야합니까? (0) | 2020.12.08 |
---|---|
셀레늄 그리드 : MaxSessions 대 MaxInstances (0) | 2020.12.08 |
Xmpp 대 Websocket (0) | 2020.12.08 |
URL 문자열에서 모의 HttpServletRequest 만들기? (0) | 2020.12.08 |
PowerMock을 사용하여 테스트를위한 비공개 방법을 모의하는 방법은 무엇입니까? (0) | 2020.12.08 |