サンプル
今回は、このようにstatic
フォルダ内のuploadフォルダ
に画像をアップロードするアプリを作っていきます。
タスクの登録画面から必要な情報を入力して、登録ボタンをクリックします。
すると、static
フォルダ内のuploadフォルダ
に画像をアップロードされます。
ディレクトリ構造
ツリー構造は下記のようになっています。
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の関数名の命名規則について忘れがちなので記載しておきます。
フロント側のコード
では、ファイルのアップロード項目を含むコードを書いていきます。
<!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>
$(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
メソッドと同じ目的を果たしますが、手動でファイルの内容を処理しています。
蒸気を踏まえて作成したコード
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("タスク登録が成功しました")
}
全体のコードは下記です。
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(®isterUser)
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(®isteredUser)
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)
}
さいごにルーティング設定です。
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{})
}
以上です。