0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Go言語】WebフレームワークBeegoを使ってファイルをアップロードする方法

Posted at

サンプル

今回は、このようにstaticフォルダ内のuploadフォルダに画像をアップロードするアプリを作っていきます。

タスクの登録画面から必要な情報を入力して、登録ボタンをクリックします。

BeegTodo登録.png

すると、staticフォルダ内のuploadフォルダに画像をアップロードされます。
image.png

ディレクトリ構造

ツリー構造は下記のようになっています。

MY-FIRST-BEEGO-PROJECT
├─ conf
│  └─ app.conf
├─ controllers
│  └─ default.go
├─ crypt
│  └─ crypt.go
├─ models
│  └─ todo.go
├─ routers
│  └─ router.go
└─ static
   ├─ css
   ├─ img
   ├─ js
   │  ├─ form.js
   │  ├─ login.js
   │  ├─ reload.min.js
   │  ├─ todoCreate.js
   │  ├─ todoCard.js
   │  └─ todoList.js
   ├─ lib
   │  └─ bootstrap
   │     ├─ css
   │     │  ├─ bootstrap-grid.min.css
   │     │  └─ bootstrap.min.css
   │     ├─ js
   │     │  ├─ bootstrap-bundle.min.js
   │     │  └─ bootstrap.min.js
   │     ├─ jquery
   │     │  └─ jquery.js
   │     └─ upload
   ├─ views
   │  ├─ mytodo
   │  │  ├─ addTodo.tpl
   │  │  └─ myList.tpl
   │  ├─ index.tpl
   │  ├─ subIndex.tpl
   │  └─ thirdIndex.tpl
   └─ main.go

Beegoの関数の命名規則

Beegoの関数名の命名規則について忘れがちなので記載しておきます。

関数名の先頭文字は大文字で以降は小文字となります。

sample.go
func (c *SampleController) Post () {
    //何らかの処理をここに記載する
}

Beego関数の命名規則について.png

フロント側のコード

では、ファイルのアップロード項目を含むコードを書いていきます。

views/mytodo/addTodo.tpl
<!DOCTYPE>
<html>
    <head>
        <title>Todo登録</title>
        <!--共通-->
        <link rel="stylesheet" type="text/css" href="/static/lib/bootstrap/css/bootstrap.min.css"/>
        <script type="text/javascript" src="/static/lib/jquery/jquery.js"></script>
        <!--個別-->
        <script type="text/javascript" src="/static/js/taskCreate.js"></script>        
    </head>
    <body>
        <h1>Todo登録</h1>
        <div class="container">
            サンプル
            <form enctype="multipart/form-data">
                <div class="form-group mb-3">
                    <label>タスク名</label>
                    <input type="text" id="myTask" name="myTask" class="form-control border border-dark" required/>
                </div>
                <div class="form-group mb-3">
                    <label for="exampleFormControlTextarea1" class="form-label">タスク詳細</label>
                    <textarea class="form-control" id="myTaskDescription" name="myTaskDescription" rows="3"></textarea>
                </div>
                <div class="form-group mb-3">
                    <label>優先度</label>
                    <select id="myTaskPriority" name="myTaskPriority" class="form-select form-select-lg mb-3" aria-label=".form-select-lg">
                        <option>-- 選択してください --</option>
                        <option value="1"></option>
                        <option value="2"></option>
                        <option value="3"></option>
                    </select>
                </div>
                <div class="form-group mb-3">
                    <label>タスク画像</label>
                    <input type="file" id="myTaskImage" name="myTaskImage" class="form-control border border-dark"/>
                </div>
                <div class="form-group mb-3">
                    <button id="todoCreate" class="btn btn-primary">
                        登録<br/><small>Create</small>
                    </button>
                    <button id="backToTopPage" class="btn btn-secondary">
                        一覧に戻る<br/><small>Back To TopPage</small>
                    </button>
                </div>
            </form>
        </div>
    </body>
</html>
static/js/taskCreate.js
$(document).ready(function(){
    //一覧に戻るボタンをクリックしたときの処理
    $('#backToTopPage').on('click',function(){
        location.replace("/myTodo/myList");
    });

    //登録ボタンをクリックしたときの処理
    $('#todoCreate').on('click',function(e){
        //POST通信するための設定
        e.preventDefault();

        //オプションタグの選択項目を取得
        const selectedTaskPriority = document.getElementById("myTaskPriority");

        //フォームデータの作成
        let formData = new FormData();
        formData.append('myTask',$("#myTask").val());
        formData.append('myTaskDescription',$("#myTaskDescription").val());
        formData.append('myTaskPriority',$("#myTaskPriority").val());   //  $("#myTaskPriority").val()
        formData.append('myTaskImage',$("#myTaskImage")[0].files[0]);
        const fileInfo = $("#myTaskImage").val();
        console.log(fileInfo);

        $.ajax({
            url:"/controllers/todoAdd",
            method:"POST",
            data:formData,
            processData:false,
            contentType:false,
            success:function(response){
                alert("タスクの登録に成功しました。");
            },
            error:function(response){
                alert("タスクの登録に失敗しました。");
            }
        });
    });

});

サーバ側のコード

ファイルアップロード機能について試行錯誤したのでメモしておきます。

SaveFile メソッドが使えない原因として、c.SaveFile が直接 beego.Controller に存在しない可能性があります。
実際、SaveFile は beego.Controller で使う場合は、ファイルの処理方法が少し異なる場合があります。

ここでは、ファイルの保存に関連する方法を説明しておきます。

解決策: c.SaveFile ではなく、c.SaveFile の代わりに os パッケージを使って直接ファイルの保存を行う方法

1. ファイルを保存するために os パッケージを使う

SaveFile メソッドを使わず、os を使ってファイルを保存する方法に変更します。

変更点の説明

os.MkdirAll を使って保存先ディレクトリを作成: static/upload/ ディレクトリが存在しない場合に作成します。これにより、ディレクトリがない場合でもエラーにならないようにします。

ファイルの保存に os.Create を使用: os.Create を使ってファイルを保存します。これにより、ファイルを保存するためのos.Fileを取得できます。

ファイルの内容をコピーする: アップロードされたファイルをf.ReadFrom(f)を使って保存します。この手順は、SaveFile メソッドと同じ目的を果たしますが、手動でファイルの内容を処理しています。

蒸気を踏まえて作成したコード

controllers/default.go
package controllers

import (
	"fmt"
	"github.com/astaxie/beego"
	"os"
	"path/filepath"
)

// タスク登録
type TodoAddController struct {
	beego.Controller
}

// タスク登録用コントローラ
func (c *TodoAddController) POST() {
	// フォームデータから入力情報を取得する
	task := c.GetString("myTask")
	taskDescription := c.GetString("myTaskDescription")
	taskPriority := c.GetString("myTaskPriority")

	fmt.Println("フォーム画面から取得したタスク名は、", task)
	fmt.Println("フォーム画面から取得したタスク説明は、", taskDescription)
	fmt.Println("フォーム画面から取得した優先度は、", taskPriority)

	// ファイルをアップロード
	f, h, err := c.GetFile("myTaskImage")
	if err != nil {
		fmt.Println("ファイルエラー", err)
		c.Ctx.ResponseWriter.WriteHeader(500)
		c.Ctx.WriteString("画像アップロード失敗")
		return
	}
	defer f.Close()

	// 保存先ディレクトリの作成
	uploadPath := "static/upload/"
	err = os.MkdirAll(uploadPath, os.ModePerm) // ディレクトリがない場合に作成
	if err != nil {
		fmt.Println("ディレクトリ作成エラー:", err)
		c.Ctx.ResponseWriter.WriteHeader(500)
		c.Ctx.WriteString("ディレクトリ作成に失敗しました")
		return
	}

	// 画像ファイル名を取得して保存
	fileName := h.Filename
	savePath := filepath.Join(uploadPath, fileName)

	// ファイルを保存
	outFile, err := os.Create(savePath)
	if err != nil {
		fmt.Println("ファイル保存エラー", err)
		c.Ctx.ResponseWriter.WriteHeader(500)
		c.Ctx.WriteString("画像保存に失敗しました")
		return
	}
	defer outFile.Close()

	// アップロードされたファイルの内容を保存
	_, err = f.Seek(0, 0) // ファイルの最初に戻す
	if err != nil {
		fmt.Println("ファイル読み込みエラー", err)
		c.Ctx.ResponseWriter.WriteHeader(500)
		c.Ctx.WriteString("ファイル読み込みに失敗しました")
		return
	}

	_, err = outFile.ReadFrom(f)
	if err != nil {
		fmt.Println("ファイル書き込みエラー", err)
		c.Ctx.ResponseWriter.WriteHeader(500)
		c.Ctx.WriteString("ファイル書き込みに失敗しました")
		return
	}

	// ファイル名を表示
	fmt.Println("ファイル名は", fileName)

	// 成功レスポンスを返す
	c.Ctx.ResponseWriter.WriteHeader(200)
	c.Ctx.WriteString("タスク登録が成功しました")
}

全体のコードは下記です。

controllers/default.go
package controllers

import (
	"fmt"
	"time"

	"my-first-beego-project/models"
	_ "my-first-beego-project/models"

	"github.com/astaxie/beego"
	"github.com/beego/beego/v2/client/orm"

	"my-first-beego-project/crypto"

	"os"
	"path/filepath"
)

type MainController struct {
	beego.Controller
}

func (c *MainController) Get() {
	c.Data["Website"] = "beego.me"
	c.Data["Email"] = "astaxie@gmail.com"
	c.TplName = "index.tpl"
}

// 20250205追加
type SubController struct {
	beego.Controller
}

type ThirdController struct {
	beego.Controller
}

type UserController struct {
	beego.Controller
}

// ログイン画面
type LoginController struct {
	beego.Controller
}

// タスクの登録画面
type TodoCreateController struct {
	beego.Controller
}

// タスク登録
type TodoAddController struct {
	beego.Controller
}

// 20250205
func (c *SubController) Get() {
	c.Data["Page"] = "SubPage"
	c.TplName = "subIndex.tpl"
}

// @router :id[get]
func (this *ThirdController) Get() {
	this.Data["ID"] = this.Ctx.Input.Param(":id")
	this.TplName = "thirdIndex.tpl"
}

// ログイン画面コントローラ
func (c *LoginController) Get() {
	c.TplName = "login.tpl"
}

func (c *UserController) Post() {
	//フォームデータを取得する
	name := c.GetString("name")
	email := c.GetString("email")
	password := c.GetString("password")

	//デバッグ用
	//fmt.Println("フォーム画面から取得したNameは:", name)
	//fmt.Println("フォーム画面から取得したEmailは:", email)
	//fmt.Println("フォーム画面から取得したPasswordは:", password)

	c.Data["json"] = map[string]interface{}{
		"status":  "成功",
		"message": "フォーム画面からデータを受け取りました。",
		"requestData": map[string]string{
			"Name":     name,
			"Email":    email,
			"Password": password,
		},
	}
	/*
		c.Data["json"] = map[string]string{
			"status":"成功",
			"message":"フォーム画面からデータを受け取りました。",
		}
	*/
	//パスワードのハッシュ化
	generatedHashedPassword, error := crypto.GeneratedPasswordHashed(password)

	if error != nil {
		fmt.Println("パスワードを暗号化中にエラーが発生しました。", error)
		c.Data["json"] = map[string]string{
			"status":  "失敗",
			"message": "パスワードのハッシュ化に失敗しました。",
		}
		c.ServeJSON()
		return
	}
	//ORMを取得する
	o := orm.NewOrm()
	//モデルTodoのインスタンスを生成
	var registerUser models.Register //var todo models.Todo
	//トランザクション開始
	tx, err := o.Begin()

	if err != nil {
		fmt.Println("トランザクション開始エラー", err)
		c.Data["json"] = map[string]string{
			"status":  "失敗",
			"message": "トランザクションの開始に失敗しました。",
		}
	}

	registerUser.Name = name
	registerUser.Email = email
	registerUser.Password = generatedHashedPassword
	registerUser.CreatedAt = time.Now()

	_, err = tx.Insert(&registerUser)
	if err != nil {
		tx.Rollback()
		fmt.Println("エラー発生", err)
		c.Data["json"] = map[string]string{
			"status":  "失敗",
			"message": "ユーザ登録に失敗しました。",
		}
		c.ServeJSON()
		return
	}
	//成功した場合コミット
	err = tx.Commit()

	if err != nil {
		fmt.Println("トランザクションのコミットに失敗しました。", err)
		c.Data["json"] = map[string]string{
			"status":  "失敗",
			"message": "トランザクションのコミットに失敗しました。",
		}
		c.ServeJSON()
		return
	}

	//登録したデータを取得する
	var registeredUser models.Register
	err = o.QueryTable("register").Filter("Email", email).One(&registeredUser)

	if err != nil {
		fmt.Println("ユーザ情報の取得に失敗しました。", err)
		c.Data["json"] = map[string]string{
			"status":  "失敗",
			"message": "ユーザー情報の取得に失敗しました。",
		}
		c.ServeJSON()
		return
	}

	fmt.Println("登録されたユーザは、", registeredUser.Name)
	fmt.Println("登録されたEmailは、", registeredUser.Email)

	//成功時のレスポンス
	c.Data["json"] = map[string]interface{}{
		"status":  "成功",
		"message": "ユーザーが正常に登録されました",
		"requestData": map[string]string{
			"Name":  registeredUser.Name,
			"Email": registeredUser.Email,
		},
	}

	//JSONデータを返却する
	c.ServeJSON()
}

func (c *LoginController) Post() {

	//フォームデータを取得する
	email := c.GetString("email")
	password := c.GetString("password")

	fmt.Println("ログイン画面から渡されたメールアドレスは、", email)
	fmt.Println("ログイン画面から渡されたパスワードは、", password)

	//ORMを定義する
	o := orm.NewOrm()

	//登録済みのデータを取得する
	var targetUser models.Register
	// `err` を宣言
	err := o.QueryTable("register").Filter("Email", email).One(&targetUser)

	if err != nil {
		fmt.Println("ユーザ情報を取得できませんでした。", err)
		c.Data["json"] = map[string]string{
			"status":  "失敗",
			"message": "ユーザ情報をしゅとくできませんでした。",
		}
		c.ServeJSON()
		return
	}
	fmt.Println("ここまで1", err)
	err = crypto.ComparedHashedPassword(targetUser.Password, password)
	fmt.Println("ここまで2", err)
	if err != nil {
		fmt.Println("パスワードが一致しませんでした。", err)
		c.Data["json"] = map[string]string{
			"status":  "失敗",
			"message": "パスワードが一致しませんでした。",
		}
		c.ServeJSON()
		return
	}
	fmt.Println("ここまで3")
	c.Data["json"] = map[string]string{
		"status":  "成功",
		"message": "ログインに成功しました。",
	}
	c.ServeJSON()
}

func (c *TodoCreateController) Get() {
	//fmt.Println("TodoCreateControllerに来た")
	c.TplName = "mytodo/addTodo.tpl" //c.TplName = "mytodo/addTodo.tpl"
	//fmt.Println("TodoCreateControllerに来たよ")
}

// タスク登録用コントローラ
func (c *TodoAddController) Post() {
	//フォームデータから入力情報を取得する
	task := c.GetString("myTask")
	taskDescription := c.GetString("myTaskDescription")
	taskPriority := c.GetString("myTaskPriority")

	fmt.Println("フォーム画面から取得したタスク名は、", task)
	fmt.Println("フォーム画面から取得したタスク説明は、", taskDescription)
	fmt.Println("フォーム画面から取得したタスク画像は、は、", taskPriority)

	//ファイルをアップロード
	f, h, err := c.GetFile("myTaskImage")
	if err != nil {
		fmt.Println("ファイルエラー", err)
		c.Ctx.ResponseWriter.WriteHeader(500)
		c.Ctx.WriteString("画像アップロード失敗")
		return
	}

	defer f.Close()
	// 画像を保存するパスを指定
	uploadPath := "static/upload/"
	err = os.MkdirAll(uploadPath, os.ModePerm) // ディレクトリがない場合に作成

	if err != nil {
		fmt.Println("ファイル保存エラー", err)
		c.Ctx.ResponseWriter.WriteHeader(500)
		c.Ctx.WriteString("画像保存失敗")
		return
	}

	// ファイル名を表示
	//fmt.Println("ファイル名は", fileName)

	// 画像ファイル名を取得して保存
	fileName := h.Filename
	savePath := filepath.Join(uploadPath, fileName)

	// ファイルを保存
	outFile, err := os.Create(savePath)
	if err != nil {
		fmt.Println("ファイル保存エラー", err)
		c.Ctx.ResponseWriter.WriteHeader(500)
		c.Ctx.WriteString("画像保存に失敗しました")
		return
	}

	defer outFile.Close()

	// アップロードされたファイルの内容を保存
	_, err = f.Seek(0, 0) // ファイルの最初に戻す
	if err != nil {
		fmt.Println("ファイル読み込みエラー", err)
		c.Ctx.ResponseWriter.WriteHeader(500)
		c.Ctx.WriteString("ファイル読み込みに失敗しました")
		return
	}

	_, err = outFile.ReadFrom(f)
	if err != nil {
		fmt.Println("ファイル書き込みエラー", err)
		c.Ctx.ResponseWriter.WriteHeader(500)
		c.Ctx.WriteString("ファイル書き込みに失敗しました")
		return
	}

	// ファイル名を表示
	fmt.Println("ファイル名は", fileName)

	// 成功レスポンスを返す
	c.Ctx.ResponseWriter.WriteHeader(200)
	c.Ctx.WriteString("タスク登録が成功しました")
	//c.SaveFile("uploadname", "static/upload/"+h.Filename)
}

さいごにルーティング設定です。

router/router.go
package routers

import (
	"my-first-beego-project/controllers"

	"github.com/astaxie/beego"
)

func init() {
	beego.Router("/", &controllers.MainController{})
	//追加
	beego.Router("/sub", &controllers.SubController{})
	beego.Router("/:id", &controllers.ThirdController{})
	beego.Router("/controllers/user", &controllers.UserController{})
	beego.Router("/login", &controllers.LoginController{})
	beego.Router("/controllers/login", &controllers.LoginController{})
	beego.Router("/mytodo/addTodo", &controllers.TodoCreateController{})
	beego.Router("/controllers/todoAdd", &controllers.TodoAddController{})
}

以上です。

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?