LoginSignup
15
10

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-02-18

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言語並にシンプルに書ける記述でした。

15
10
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
10