From 78eaf7d273d136d756e9898bc5ec140b10103aa1 Mon Sep 17 00:00:00 2001 From: Shin Kojima Date: Wed, 5 Jun 2013 00:12:31 +0900 Subject: [PATCH] [ja] Translate 04.5.md into Japanese. --- ja/ebook/04.5.md | 156 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 ja/ebook/04.5.md diff --git a/ja/ebook/04.5.md b/ja/ebook/04.5.md new file mode 100644 index 00000000..98080934 --- /dev/null +++ b/ja/ebook/04.5.md @@ -0,0 +1,156 @@ +# 4.5 ファイルのアップロード処理 +ユーザによるファイルのアップロードを処理したいとします。例えば、現在Instagramのようなホームページを作成しているとします。ユーザが撮影した写真を保存する必要があります。このような要求はどのように実現するのでしょうか? + +フォームにファイルをアップロードさせるためには、まずformの`enctype`属性を追加する必要があります。`enctype`属性には以下の3つの状態あります: + + application/x-www-form-urlencoded 送信前にすべての文字列をエンコードする(デフォルト) + multipart/form-data 文字列に対してエンコードしません。ファイルのアップロードウィジェットを含むフォームを使用するときはこの値が必要です。 + text/plain 空白を"+"記号に置き換えます。ただし、特殊文字に対してエンコードは行われません。 + +そのため、フォームのhtmlコードはこのようになります: + + + + ファイルアップロード + + +
+ + + +
+ + + +サーバでは、handlerFuncをひとつ追加します: + + http.HandleFunc("/upload", upload) + + // /uploadを処理するロジック + 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) + } + } + +上のコードでは、ファイルのアップロードを処理するためには`r.ParseMultipartForm`をコールする必要があります。引数には`maxMemory`が表示されています。`ParseMultipartForm`をコールした後、アップロードするファイルは`maxMemory`のサイズのメモリに保存されます。もしファイルのサイズが`maxMemory`を超えた場合、残った部分はシステムのテンポラリファイルに保存されます。`r.FormFile`によって上のファイルハンドルを取得することができます。その後実例の中では`io.Copy`を使ってファイルを保存しています。 + +>その他のファイルではないフィールド情報を取得する時は`r.ParseForm`をコールする必要はありません。必要な時はGoが自動でコールします。また`ParseMultipartFrom`を一度コールすると、その後にもう一度コールしても効果はありません。 + +上の実例を通して、ファイルのアップロードには主に3ステップの処理があることが分かります: + +1. フォームにenctype="multipart/form-data"を追加する。 +2. サーバで`r.ParseMultipartForm`をコールし、アップロードするファイルをメモリとテンポラリファイルに保存する。 +3. `r.FormFile`を使用して、ファイルハンドルを取得し、ファイルに対して保存等の処理を行う。 + +ファイルhandlerはmultipart.FileHnadlerです。この中には以下のような構造体が保存されています。 + + type FileHeader struct { + Filename string + Header textproto.MIMEHeader + // contains filtered or unexported fields + } + +上のコード例では以下のようにファイルのアップロードを出力します。 + +![](images/4.5.upload2.png?raw=true) + +図4.5 ファイルのアップロードを行った後サーバが受け取った情報の出力 + +## クライアントによるファイルのアップロード + +上の例でどのようにフォームからファイルをアップロードするのか示しました。その後サーバでファイルを処理しますが、Goは実はクライアントのフォームによるファイルのアップロードをエミュレートする機能をサポートしています。詳しい仕様方法は以下の例をご覧ください: + + 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) + + //キーとなる操作 + fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename) + if err != nil { + fmt.Println("error writing to buffer") + return err + } + + //ファイルハンドル操作をオープンする + 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 + } + + // sample usage + func main() { + target_url := "http://localhost:9090/upload" + filename := "./astaxie.pdf" + postFile(filename, target_url) + } + + +上の例ではクライアントが如何にサーバに対し一つのファイルをアップロードするのかご説明しました。クライアントはmultipart.Writeを通してファイルの本文をバッファの中に書き込みます。その後、httpのPostメソッドをコールしてバッファからサーバに転送します。 + +>もしあなたが他にusernameといった普通のフィールドを同時に書き込む場合は、multipartのWriteFieldメソッドをコールして、その他の似たようなフィールドを複数書き込むことができます。 + +## links + * [目次]() + * 前へ: [フォームの多重送信の防止](<04.4.md>) + * 次へ: [サマリー](<04.6.md>) +