Help us understand the problem. What is going on with this article?

Go言語でのファイルアップロード

More than 1 year has passed since last update.

Go言語でHTMLページからファイルアップロードされた場合,
サーバー側の任意の場所にアップされたファイルを保存するサンプル。

sever.go
package main



import (format "fmt");
import ("net/http");
import "os";
import "html/template";
import "mime/multipart";
func main () {

    var mux *http.ServeMux;
    mux = http.NewServeMux();
    mux.HandleFunc("/hello", func (writer http.ResponseWriter, r *http.Request) {
        format.Fprintln(writer, "HandleFuncメソッドにServeHTTPを関数と同じシグネチャをそのまま渡す");
    })
    var hf http.HandlerFunc;
    hf = func (writer http.ResponseWriter, request *http.Request) {
        format.Fprintln(writer, "HandlerFunc型を定義->Handleメソッドに渡す");
    }
    mux.Handle("/hf", hf);
    mux . HandleFunc("/upload", upload);
    mux . HandleFunc("/", index);
    var myHandler = new(MyHandle);

    mux.Handle("/handle", myHandler);
    // http.Server構造体のポインタを宣言
    var server *http.Server;
    // http.Serverのオブジェクトを確保
    // &をつけること構造体ではなくポインタを返却
    server = &http.Server{}; // or new (http.Server);
    server.Addr = ":11180";
    server.Handler = mux;
    server.ListenAndServe();
}

type MyHandle struct {
    //とりあえず中身は空っぽ
}
func (this *MyHandle) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
    format.Fprintln(writer, "http.Handler型インターフェースを実装したオブジェクトをhttp.Handleに渡す");
};


func index (writer http.ResponseWriter , request *http.Request) {
    var t *template.Template;
    /*
<!-- テンプレートの中身 -->
<!DOCTYPE html>
<!-- template用のhtmlファイル -->
<html>
<head>
    <title>ファイルアップロードテスト</title>
</head>
<body>
<div class="container">
    <h1>ファイルアップロードテスト</h1>
    <form method="post" action="http://localhost:11180/upload" enctype="multipart/form-data">
        <fieldset>
            <input type="file" name="image" id="upload_files" multiple="multiple">
            <input type="submit" name="submit" value="アップロード開始">
        </fieldset>
    </form>
</div>
</body>
</html>
    */
    // テンプレートをロード
    t, _ = template.ParseFiles("template/index.html");
    t.Execute(writer, struct{}{});
}
func upload ( w http.ResponseWriter, r *http.Request) {
    // このハンドラ関数へのアクセスはPOSTメソッドのみ認める
    if  (r.Method != "POST") {
        format.Fprintln(w, "許可したメソッドとはことなります。");
        return;
    }
    var file multipart.File;
    var fileHeader *multipart.FileHeader;
    var e error;
    var uploadedFileName string;
    var img []byte = make([]byte, 1024);
    // POSTされたファイルデータを取得する
    file , fileHeader , e = r.FormFile ("image");
    if (e != nil) {
        format.Fprintln(w, "ファイルアップロードを確認できませんでした。");
        return;
    }
    uploadedFileName = fileHeader.Filename;
    // サーバー側に保存するために空ファイルを作成
    var saveImage *os.File;
    saveImage, e = os.Create("./" + uploadedFileName);
    if (e != nil) {
        format.Fprintln(w, "サーバ側でファイル確保できませんでした。");
        return;
    }
    defer saveImage.Close();
    defer file.Close();
    var tempLength int64 =0;
    for {
        // 何byte読み込んだかを取得
        n , e := file.Read(img);
        // 読み混んだバイト数が0を返したらループを抜ける
        if (n == 0) {
            format.Println(e);
            break;
        }
        if (e != nil) {
            format.Println(e);
            format.Fprintln(w, "アップロードされたファイルデータのコピーに失敗。");
            return;
        }
        saveImage.WriteAt(img, tempLength);
        tempLength = int64(n) + tempLength;
    }
    format.Fprintf(w, "文字列HTTPとして出力させる");
}


Go言語のハンドラには,
http.Handler型インターフェースが持つ,ServeHTTPと同じシグネチャをもつ関数を渡せる
HandleFuncメソッドと
http.Handler型インターフェースにマッチするオブジェクトを渡すことで動作する
Handleメソッドの2つがある。
上記の旨と,ファイルアップ時のサーバー側での処理。
アップしたファイルの処理は好きに処理できそうだけれど,上記サンプルでは愚直にReadメソッドで指定バイトずつ
スライスに確保。

また,コメントにioライブラリのCopy関数を使うとよいと指摘があったので以下のように修正もできる。

server.go
func upload ( w http.ResponseWriter, r *http.Request) {
    // このハンドラ関数へのアクセスはPOSTメソッドのみ認める
    if  (r.Method != "POST") {
        format.Fprintln(w, "許可したメソッドとはことなります。");
        return;
    }
    var file multipart.File;
    var fileHeader *multipart.FileHeader;
    var e error;
    var uploadedFileName string;
    // POSTされたファイルデータを取得する
    file , fileHeader , e = r.FormFile ("image");
    format.Printf("%T", file);
    if (e != nil) {
        format.Fprintln(w, "ファイルアップロードを確認できませんでした。");
        return;
    }
    uploadedFileName = fileHeader.Filename;
    // サーバー側に保存するために空ファイルを作成
    var saveImage *os.File;
    saveImage, e = os.Create("./" + uploadedFileName);
    if (e != nil) {
        format.Fprintln(w, "サーバ側でファイル確保できませんでした。");
        return;
    }
    defer saveImage.Close();
    defer file.Close();
    size, e := io.Copy(saveImage, file);
    if (e != nil) {
        format.Println(e);
        format.Println("アップロードしたファイルの書き込みに失敗しました。");
        os.Exit(1);
    }
    format.Println("書き込んだByte数=>");
    format.Println(size);
    format.Fprintf(w, "文字列HTTPとして出力させる");
}


かなりio.Copy...かなりLL言語並にシンプルに書ける記述でした。

1000VICKY
とある福岡の地場企業
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした