Go言語によるWebサーバー作成入門 ーブログ作成ー
(作成2020.11.2)
(実践)ブログを作成する
これまで紹介してきたことを使ってブログを作成します。テンプレートをindex.gohtmlとして作成し、main.goにて実行しサーバーを立ち上げます。テンプレートをParseFilesで読み込んでExecuteで出力します。ルートにHandleFuncを割り当ててindex関数を呼び出します。favicon.icoはタブについている小さなイメージでしたね。これはNotFoundHandlerで無視します。サーバーの立ち上げはListenAndServeです。DefaultServeMuxを使うので、第2引数にはnilを指定します。もし、この説明でわからないところがあれば、過去の記事を見返してください。
package main
import (
"html/template"
"net/http"
)
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseFiles("index.gohtml"))
}
func main() {
http.HandleFunc("/", index)
http.Handle("/favicon.ico", http.NotFoundHandler())
http.ListenAndServe(":8080", nil)
}
func index(w http.ResponseWriter, req *http.Request) {
tpl.Execute(w, nil)
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>INDEX</title>
</head>
<body>
Hello from index
</body>
</html>
Cookieを作成する
getCookie関数を用意してCookieを作成します。作成はSetCookieでできるのでしたね。Cookieはstructなのでポインタを使って渡します。ユニークIDはサードパーティパッケージgithub.com/google/uuid を使うのでしたね。github.com/satori/go.uuid を使ってもよいです。作成したIDをテンプレートに渡しています。localhost:8080にアクセスしたら「検証-Application」からCookieができていることを確認しましょう。
package main
import (
"html/template"
"net/http"
"github.com/google/uuid"
)
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseFiles("index.gohtml"))
}
func main() {
http.HandleFunc("/", index)
http.Handle("/favicon.ico", http.NotFoundHandler())
http.ListenAndServe(":8080", nil)
}
func index(w http.ResponseWriter, req *http.Request) {
c := getCookie(w, req) // Cookie構造体のポインタを取得する
tpl.Execute(w, c.Value) // Valueをテンプレートに渡す
}
// cookieを取得する関数を定義
func getCookie(w http.ResponseWriter, req *http.Request) *http.Cookie {
c, err := req.Cookie("session")
// Cookieがなければ作る
if err != nil {
sID := uuid.New()
c = &http.Cookie{
Name: "session",
Value: sID.String(),
}
http.SetCookie(w, c)
}
return c
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>INDEX</title>
</head>
<body>
<h1>Cookie Value: {{.}}</h1>
</body>
</html>
画像を並べる
画像を表示します。とりあえず、画像を用意するのはおいといてテンプレートに複数の画像ファイルをうまく渡せるかどうか試してみます。複数の項目を渡すときには{{range .}}{{end}}を使うのでしたね。Cookieにも画像ファイル名を渡してみます。
package main
import (
"html/template"
"net/http"
"strings"
"github.com/google/uuid"
)
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseFiles("index.gohtml"))
}
func main() {
http.HandleFunc("/", index)
http.Handle("/favicon.ico", http.NotFoundHandler())
http.ListenAndServe(":8080", nil)
}
func index(w http.ResponseWriter, req *http.Request) {
c := getCookie(w, req)
c = appendValue(w, c) // ファイル名をつなぐ
xs := strings.Split(c.Value, "|") // スライス[]string
tpl.Execute(w, xs)
}
func getCookie(w http.ResponseWriter, req *http.Request) *http.Cookie {
c, err := req.Cookie("session")
if err != nil {
sID := uuid.New()
c = &http.Cookie{
Name: "session",
Value: sID.String(),
}
http.SetCookie(w, c)
}
return c
}
func appendValue(w http.ResponseWriter, c *http.Cookie) *http.Cookie {
// TODO: files
p1 := "dog.jpg"
p2 := "cat.jpg"
p3 := "SeaOtter.jpg"
// append
s := c.Value
if !strings.Contains(s, p1) {
s += "|" + p1
}
if !strings.Contains(s, p2) {
s += "|" + p2
}
if !strings.Contains(s, p3) {
s += "|" + p3
}
c.Value = s
http.SetCookie(w, c) // Cookieにセットしておく
return c
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>INDEX</title>
</head>
<body>
<h1>Cookie Values:</h1>
{{range .}}
<h2>{{.}}</h2>
{{end}}
</body>
</html>
ファイルをアップロードする
formを使って画像ファイルをアップロードします。ファイル名はハッシュsha256を使って作ります。ファイルアップロードは、os.Createで空のファイルを作成してコピーすることで実行します。
package main
import (
"crypto/sha256"
"fmt"
"html/template"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/google/uuid"
)
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseFiles("index.gohtml"))
}
func main() {
http.HandleFunc("/", index)
http.Handle("/favicon.ico", http.NotFoundHandler())
http.ListenAndServe(":8080", nil)
}
func index(w http.ResponseWriter, req *http.Request) {
c := getCookie(w, req)
// form data recieved
if req.Method == http.MethodPost {
mf, fh, err := req.FormFile("nf")
if err != nil {
fmt.Println(err)
}
defer mf.Close()
ext := strings.Split(fh.Filename, ".")[1] // 拡張子
h := sha256.New() // ファイルを保存するときの名前を作る
io.Copy(h, mf)
fname := fmt.Sprintf("%x", h.Sum(nil)) + "." + ext
wd, err := os.Getwd() // get local path
if err != nil {
fmt.Println(err)
}
path := filepath.Join(wd, "pics", fname)
nf, err := os.Create(path) // まず空ファイルを作る
if err != nil {
fmt.Println(err)
}
defer nf.Close()
mf.Seek(0, 0)
io.Copy(nf, mf) // アップロードファイルのデータを空ファイルにコピー
c = appendValue(w, c, fname) // ファイル名作成は関数の外に出した
}
xs := strings.Split(c.Value, "|")
tpl.Execute(w, xs)
}
func getCookie(w http.ResponseWriter, req *http.Request) *http.Cookie {
c, err := req.Cookie("session")
if err != nil {
sID := uuid.New()
c = &http.Cookie{
Name: "session",
Value: sID.String(),
}
http.SetCookie(w, c)
}
return c
}
func appendValue(w http.ResponseWriter, c *http.Cookie, fname string) *http.Cookie {
s := c.Value
if !strings.Contains(s, fname) {
s += "|" + fname
}
c.Value = s
http.SetCookie(w, c)
return c
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>INDEX</title>
</head>
<body>
<h1>Cookie Values:</h1>
{{range .}}
<h2>{{.}}</h2>
{{end}}
<form method="POST" enctype="multipart/form-data">
<input type="file" name="nf">
<input type="submit">
</form>
</body>
</html>
画像を表示する
ここまでくればあとはhtmlにimgタグをつけて表示すれば完成です。ファイル名をクッキーから受け取るところにSplitを使ってバラしています。
package main
import (
"crypto/sha256"
"fmt"
"html/template"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/google/uuid"
)
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseFiles("index.gohtml"))
}
func main() {
http.HandleFunc("/", index)
http.Handle("/pics/", http.StripPrefix("/pics", http.FileServer(http.Dir("./pics"))))
http.Handle("/favicon.ico", http.NotFoundHandler())
http.ListenAndServe(":8080", nil)
}
func index(w http.ResponseWriter, req *http.Request) {
c := getCookie(w, req)
if req.Method == http.MethodPost {
mf, fh, err := req.FormFile("nf")
if err != nil {
fmt.Println(err)
}
defer mf.Close()
ext := strings.Split(fh.Filename, ".")[1]
h := sha256.New()
io.Copy(h, mf)
fname := fmt.Sprintf("%x", h.Sum(nil)) + "." + ext
wd, err := os.Getwd()
if err != nil {
fmt.Println(err)
}
path := filepath.Join(wd, "pics", fname)
nf, err := os.Create(path)
if err != nil {
fmt.Println(err)
}
defer nf.Close()
mf.Seek(0, 0)
io.Copy(nf, mf)
c = appendValue(w, c, fname)
}
xs := strings.Split(c.Value, "|")
tpl.Execute(w, xs[1:]) // ファイル名だけを取り出す
}
func getCookie(w http.ResponseWriter, req *http.Request) *http.Cookie {
c, err := req.Cookie("session")
if err != nil {
sID := uuid.New()
c = &http.Cookie{
Name: "session",
Value: sID.String(),
}
http.SetCookie(w, c)
}
return c
}
func appendValue(w http.ResponseWriter, c *http.Cookie, fname string) *http.Cookie {
s := c.Value
if !strings.Contains(s, fname) {
s += "|" + fname
}
c.Value = s
http.SetCookie(w, c)
return c
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>INDEX</title>
</head>
<body>
<h1>Uploaded Pictures:</h1>
{{range .}}
<img src="/pics/{{.}}" width="300px">
{{end}}
<form method="post" enctype="multipart/form-data">
<input type="file" name="nf">
<input type="submit">
</form>
</body>
</html>
画像ファイルを2つアップロードしてみました。
これまでの記事でこれだけのことができます。わからないところがあれば、過去の記事を探して確認してみてください。
Discussion