Introduction#

  • S3 client
  • Bedrock client
  • PostgreSQL and GORM as an ORM
  • PostgreSQL pure driver pq

S3 Client#

  • Upload a file to S3
  • List bucket

First let create a S3 client

var client *s3.Client
func init () {
config, error := config.LoadDefaultConfig(context.TODO())
if error != nil {
fmt.Println("ERROR")
}
client = s3.NewFromConfig(config)
}

Let call an api to list bucket

func listBuckets() {
result, error := client.ListBuckets(context.TODO(), &s3.ListBucketsInput{})
if error != nil {
fmt.Println("ERROR")
}
for _, bucket := range result.Buckets {
fmt.Println(*bucket.Name)
}
}

Or upload an object to S3

resp, error := client.PutObject(
context.TODO(),
&s3.PutObjectInput{
Bucket: aws.String("cdk-entest-videos"),
Key: aws.String("golang.jpeg"),
Body: bytes.NewReader(data),
},
)

This is the full example

main.go
package main
import (
"bytes"
"context"
"fmt"
"os"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
var client *s3.Client
func init () {
config, error := config.LoadDefaultConfig(context.TODO())
if error != nil {
fmt.Println("ERROR")
}
client = s3.NewFromConfig(config)
}
func listBuckets() {
result, error := client.ListBuckets(context.TODO(), &s3.ListBucketsInput{})
if error != nil {
fmt.Println("ERROR")
}
for _, bucket := range result.Buckets {
fmt.Println(*bucket.Name)
}
}
func main() {
data, error := os.ReadFile("dolphin.jpeg")
if error != nil {
fmt.Println("ERROR")
}
resp, error := client.PutObject(
context.TODO(),
&s3.PutObjectInput{
Bucket: aws.String("cdk-entest-videos"),
Key: aws.String("golang.jpeg"),
Body: bytes.NewReader(data),
},
)
if error != nil {
fmt.Println("ERROR")
}
fmt.Println(resp)
}

Bedrock Client#

First let define struct for Request and Response

type Request struct {
Prompt string `json:"prompt"`
MaxTokensToSample int `json:"max_tokens_to_sample"`
Temperature float64 `json:"temperature,omitempty"`
TopP float64 `json:"top_p,omitempty"`
TopK int `json:"top_k,omitempty"`
StopSequences []string `json:"stop_sequences,omitempty"`
}
type Response struct {
Completion string `json:"completion"`
}
type HelloHandler struct{}

Then, let create a bedrock client

// promt format
const claudePromptFormat = "\n\nHuman: %s\n\nAssistant:"
// bedrock runtime client
var brc *bedrockruntime.Client
// init bedorck credentials connecting to aws
func init() {
region := os.Getenv("AWS_REGION")
if region == "" {
region = "us-east-1"
}
cfg, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(region))
if err != nil {
log.Fatal(err)
}
brc = bedrockruntime.NewFromConfig(cfg)
}

Let call a request with streaming response

func bedrock() {
prompt := "" + fmt.Sprintf(claudePromptFormat, "How to cook chicken soup?")
payload := Request{
Prompt: prompt,
MaxTokensToSample: 2048,
}
payloadBytes, error := json.Marshal(payload)
if error != nil {
fmt.Println(error)
}
output, error := brc.InvokeModelWithResponseStream(
context.Background(),
&bedrockruntime.InvokeModelWithResponseStreamInput{
Body: payloadBytes,
ModelId: aws.String("anthropic.claude-v2"),
ContentType: aws.String("application/json"),
},
)
if error != nil {
fmt.Println(error)
}
for event := range output.GetStream().Events() {
switch v := event.(type) {
case *types.ResponseStreamMemberChunk:
var resp Response
err := json.NewDecoder(bytes.NewReader(v.Value.Bytes)).Decode(&resp)
if err != nil {
fmt.Println(err)
}
fmt.Println(resp.Completion)
case *types.UnknownUnionMember:
fmt.Println("unknown tag:", v.Tag)
default:
fmt.Println("union is nil or unknown type")
}
}
}

This is the full example

main.go
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"log"
"os"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/bedrockruntime"
"github.com/aws/aws-sdk-go-v2/service/bedrockruntime/types"
)
type Request struct {
Prompt string `json:"prompt"`
MaxTokensToSample int `json:"max_tokens_to_sample"`
Temperature float64 `json:"temperature,omitempty"`
TopP float64 `json:"top_p,omitempty"`
TopK int `json:"top_k,omitempty"`
StopSequences []string `json:"stop_sequences,omitempty"`
}
type Response struct {
Completion string `json:"completion"`
}
// promt format
const claudePromptFormat = "\n\nHuman: %s\n\nAssistant:"
// bedrock runtime client
var brc *bedrockruntime.Client
// init bedorck credentials connecting to aws
func init() {
region := os.Getenv("AWS_REGION")
if region == "" {
region = "us-east-1"
}
cfg, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(region))
if err != nil {
log.Fatal(err)
}
brc = bedrockruntime.NewFromConfig(cfg)
}
func main() {
fmt.Println("Hello Bedrock")
bedrock()
}
func bedrock() {
prompt := "" + fmt.Sprintf(claudePromptFormat, "How to cook chicken soup?")
payload := Request{
Prompt: prompt,
MaxTokensToSample: 2048,
}
payloadBytes, error := json.Marshal(payload)
if error != nil {
fmt.Println(error)
}
output, error := brc.InvokeModelWithResponseStream(
context.Background(),
&bedrockruntime.InvokeModelWithResponseStreamInput{
Body: payloadBytes,
ModelId: aws.String("anthropic.claude-v2"),
ContentType: aws.String("application/json"),
},
)
if error != nil {
fmt.Println(error)
}
for event := range output.GetStream().Events() {
switch v := event.(type) {
case *types.ResponseStreamMemberChunk:
var resp Response
err := json.NewDecoder(bytes.NewReader(v.Value.Bytes)).Decode(&resp)
if err != nil {
fmt.Println(err)
}
fmt.Println(resp.Completion)
case *types.UnknownUnionMember:
fmt.Println("unknown tag:", v.Tag)
default:
fmt.Println("union is nil or unknown type")
}
}
}

Gorm PostgreSQL#

  • Create a db connection
  • Query an existing table
  • Create model, table, CRUD

First let create a database connection

const HOST = "
const USER = "postgresql"
const DBNAME = "demo"
const PASS = ""
dsn := fmt.Sprintf("host=%v port=%v user=%v password=%v dbname=%v", HOST, "5432", USER, PASS, DBNAME)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
NoLowerCase: false,
SingularTable: true,
},
})

Second, assume that there is already an existing table named Actor, let query it. We need to specify table name.

[!IMPORTANT] Please check the syntax for table and column name, first character should be cap.

type Actor struct {
Actor_id string `gorm:"PrimaryKey"`
First_name string
Last_name string
Last_update string
}
type Tabler interface {
TableName() string
}
func (Actor) TableName() string {
return "actor"
}

Then let query Actor table

func getActors(db *gorm.DB) {
var actors []Actor
db.Limit(2).Find(&actors)
for _, actor := range actors {
fmt.Println(actor.Actor_id)
fmt.Printf(actor.First_name)
fmt.Println(actor.Last_name)
fmt.Println(actor.Last_update)
}
var actor Actor
db.First(&actor)
fmt.Println(actor.Actor_id)
fmt.Printf(actor.First_name)
fmt.Println(actor.Last_name)
fmt.Println(actor.Last_update)
}

Create a new model named Book

type Book struct {
ID uint
Title string
Author string
Amazon string
Image string
Description string
}

Then create function to do CRUP

// create table
func createTable(db *gorm.DB) {
db.AutoMigrate(&Book{})
}
// read data
func readTable(db *gorm.DB) {
var books []Book
db.Limit(10).Find(&books)
for _, book := range books {
fmt.Println(book.Author+", ", book.Title)
}
}
// insert data
func insertData(db *gorm.DB) {
db.Create(&Book{
Title: "Database Internals",
Author: "Hai Tran",
Amazon: "",
Description: "Hello",
Image: "",
})
}
// update data
func updateData(db *gorm.DB) {
db.Model(&Book{}).Where("id = ?", "2").Update("Image", "golang-idiomatic.jpeg")
}
// delete row
func deleteRows(db *gorm.DB) {
// delete book where id=5
// db.Delete(&Book{}, 4)
var books []Book
db.Delete(&books, []int{1})
}

Here is the full example

main.go
// haimtran 08/01/2024
// getting started with golang gorm orm
// psql -h database-1.c9y4mg20eppz.ap-southeast-1.rds.amazonaws.com -p 5432 -U postgresql -d demo
package main
import (
"fmt"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
type Actor struct {
Actor_id string `gorm:"PrimaryKey"`
First_name string
Last_name string
Last_update string
}
type Tabler interface {
TableName() string
}
func (Actor) TableName() string {
return "actor"
}
type Book struct {
ID uint
Title string
Author string
Amazon string
Image string
Description string
}
// create table
func createTable(db *gorm.DB) {
db.AutoMigrate(&Book{})
}
// delete row
func deleteRows(db *gorm.DB) {
// delete book where id=5
// db.Delete(&Book{}, 4)
var books []Book
db.Delete(&books, []int{1})
}
// insert data
func insertData(db *gorm.DB) {
db.Create(&Book{
Title: "Database Internals",
Author: "Hai Tran",
Amazon: "",
Description: "Hello",
Image: "",
})
}
// update data
func updateData(db *gorm.DB) {
db.Model(&Book{}).Where("id = ?", "2").Update("Image", "golang-idiomatic.jpeg")
}
// read data
func readTable(db *gorm.DB) {
var books []Book
db.Limit(10).Find(&books)
for _, book := range books {
fmt.Println(book.Author+", ", book.Title)
}
}
// read table
func getActors(db *gorm.DB) {
var actors []Actor
db.Limit(2).Find(&actors)
for _, actor := range actors {
fmt.Println(actor.Actor_id)
fmt.Printf(actor.First_name)
fmt.Println(actor.Last_name)
fmt.Println(actor.Last_update)
}
var actor Actor
db.First(&actor)
fmt.Println(actor.Actor_id)
fmt.Printf(actor.First_name)
fmt.Println(actor.Last_name)
fmt.Println(actor.Last_update)
}
const HOST = "database-1.c9y4mg20eppz.ap-southeast-1.rds.amazonaws.com"
const USER = "postgresql"
const DBNAME = "demo"
const PASS = "Admin2024"
func main() {
dsn := fmt.Sprintf("host=%v port=%v user=%v password=%v dbname=%v", HOST, "5432", USER, PASS, DBNAME)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
NoLowerCase: false,
SingularTable: true,
},
})
if err != nil {
fmt.Println("ERROR")
}
// getActors(db)
// createTable(db)
insertData(db)
// readTable(db)
// updateData(db)
// deleteRows(db)
}

PostgreSQL Driver#

Let use a pure driver to send query to database

func getBooks(db *sql.DB) []Book {
rows, error := db.Query("SELECT * FROM book LIMIT 10;")
if error != nil {
log.Fatal(error)
}
// wait untill all surrouding functions finish and run rows.Close()
defer rows.Close()
var books []Book
for rows.Next() {
var book Book
if error := rows.Scan(&book.ID, &book.Title, &book.Author, &book.Description, &book.Image, &book.Amazon); error != nil {
fmt.Println(error)
}
fmt.Print(book)
books = append(books, book)
}
fmt.Println(books)
return books
}

Here is the full code

main.go
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq"
)
type Actor struct {
actor_id int
first_name string
last_name string
last_update string
}
type Book struct {
ID uint
Author string
Title string
Image string
Description string
Amazon string
}
func main() {
fmt.Println("Hello")
connStr := "host=localhost port=5432 user=demo dbname=dvdrental password='Mike@865525' sslmode=disable"
db, error := sql.Open("postgres", connStr)
if error != nil {
log.Fatal(error)
}
rows, error := db.Query("SELECT * FROM actor LIMIT 10;")
if error != nil {
log.Fatal(error)
}
// wait untill all surrouding functions finish and run rows.Close()
defer rows.Close()
var actors []Actor
for rows.Next() {
var actor Actor
if err := rows.Scan(&actor.actor_id, &actor.first_name, &actor.last_name, &actor.last_update); err != nil {
fmt.Println(err)
}
fmt.Println(actor)
actors = append(actors, actor)
}
fmt.Println(actors)
//
getBooks(db)
}
func getBooks(db *sql.DB) []Book {
rows, error := db.Query("SELECT * FROM book LIMIT 10;")
if error != nil {
log.Fatal(error)
}
// wait untill all surrouding functions finish and run rows.Close()
defer rows.Close()
var books []Book
for rows.Next() {
var book Book
if error := rows.Scan(&book.ID, &book.Title, &book.Author, &book.Description, &book.Image, &book.Amazon); error != nil {
fmt.Println(error)
}
fmt.Print(book)
books = append(books, book)
}
fmt.Println(books)
return books
}

HTTP Server#

  • Create web server
  • Create multiplexer and handlers
  • Serve static file
  • Response json
  • Upload file

Let create a http server listening on port 3000

server := &http.Server{
Addr: ":3000",
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Fatal(server.ListenAndServe())

Create multiplexer and handlers

mux := http.NewServeMux()
mux.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) {
data := showBooks()
w.Header().Set("Content-Type", "application/json")
w.Write(data)
})
mux.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
http.ServeFile(w, r, "upload.html")
case "POST":
uploadFile(w, r)
}
})

Upload file handler

func uploadFile(w http.ResponseWriter, r *http.Request) {
// maximum upload file of 10 MB files
r.ParseMultipartForm(10 << 20)
// Get handler for filename, size and heanders
file, handler, error := r.FormFile("myFile")
if error != nil {
fmt.Println("Error")
fmt.Println(error)
return
}
defer file.Close()
fmt.Printf("upload file %v\n", handler.Filename)
fmt.Printf("file size %v\n", handler.Size)
fmt.Printf("MIME header %v\n", handler.Header)
// Create file
dest, error := os.Create(handler.Filename)
if error != nil {
return
}
defer dest.Close()
// Copy uploaded file to dest
if _, err := io.Copy(dest, file); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "Successfully Uploaded File\n")
}

Show books logic

func showBooks() []byte {
book := Book{
ID: 1,
Title: "Golang",
Author: "Hai",
Description: "Hello",
Amazon: "",
Image: "",
}
// encode to []byte
js, error := json.Marshal(book)
if error != nil {
log.Fatal(error)
}
fmt.Println(js)
// decode bytes
var x Book
json.Unmarshal(js, &x)
fmt.Println(x)
return js
}

This is full code

go.main
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"time"
)
type Book struct {
ID uint
Author string
Title string
Image string
Description string
Amazon string
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) {
data := showBooks()
w.Header().Set("Content-Type", "application/json")
w.Write(data)
})
mux.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
http.ServeFile(w, r, "upload.html")
case "POST":
uploadFile(w, r)
}
})
// create server
server := &http.Server{
Addr: ":3000",
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Fatal(server.ListenAndServe())
}
func uploadFile(w http.ResponseWriter, r *http.Request) {
// maximum upload file of 10 MB files
r.ParseMultipartForm(10 << 20)
// Get handler for filename, size and heanders
file, handler, error := r.FormFile("myFile")
if error != nil {
fmt.Println("Error")
fmt.Println(error)
return
}
defer file.Close()
fmt.Printf("upload file %v\n", handler.Filename)
fmt.Printf("file size %v\n", handler.Size)
fmt.Printf("MIME header %v\n", handler.Header)
// Create file
dest, error := os.Create(handler.Filename)
if error != nil {
return
}
defer dest.Close()
// Copy uploaded file to dest
if _, err := io.Copy(dest, file); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "Successfully Uploaded File\n")
}
func showBooks() []byte {
book := Book{
ID: 1,
Title: "Golang",
Author: "Hai",
Description: "Hello",
Amazon: "",
Image: "",
}
// encode to []byte
js, error := json.Marshal(book)
if error != nil {
log.Fatal(error)
}
fmt.Println(js)
// decode bytes
var x Book
json.Unmarshal(js, &x)
fmt.Println(x)
return js
}

Echo Framework#

  • Create a web server
  • Handle upload file
  • Handle simple get request
// haimtran 07/01/2024
// echo webserver get, post, form, upload
package main
import (
"context"
"fmt"
"io"
"net/http"
"os"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/labstack/echo/v4"
)
var client *s3.Client
func init () {
config, error := config.LoadDefaultConfig(context.TODO())
if error != nil {
fmt.Println("ERROR")
}
client = s3.NewFromConfig(config)
}
func main() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello Hai")
})
// path parameter
e.GET("/user/:id", func(c echo.Context) error {
id := c.Param("id")
return c.String(http.StatusOK, id)
})
// query parameter
e.GET("/show", func(c echo.Context) error {
name := c.QueryParam("name")
return c.String(http.StatusOK, "hello: " + name)
})
// form
// curl -d "name=hai" -d "email=minh@gmail.com" http://localhost:3000/save
e.POST("/save", func(c echo.Context) error {
name := c.FormValue("name")
email := c.FormValue("email")
return c.String(http.StatusOK, "name: " + name + "email: " + email)
})
// curl -F "name=Joe Smith" -F "avatar=dolphin.jpeg" http://localhost:3000/upload
e.POST("/upload", func(c echo.Context) error {
// get name
name := c.FormValue("name")
// get avatar
avatar, error := c.FormFile("avatar")
if error != nil {
return error
}
// source
src, error := avatar.Open()
if error != nil {
return error
}
defer src.Close()
// destination
dest, error := os.Create(avatar.Filename)
if error != nil {
return error
}
defer dest.Close()
// upload file to s3
_, error = client.PutObject(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String("cdk-entest-videos"),
Key: aws.String("golang/dolphin.jpeg"),
Body: src,
})
if error != nil {
fmt.Println("error upload to s3")
}
// copy
if _, error = io.Copy(dest, src); error != nil {
return error
}
return c.HTML(
http.StatusOK,
"<b>Thank you! " + name + "</b>",
)
})
e.Logger.Fatal(e.Start(":3000"))
}

Encoding Json#

  • Encode to byte array
  • Decode byte array to string
  • Json Ecoder and Decoder
package main
import (
"encoding/json"
"fmt"
"log"
)
type Book struct {
ID uint
Author string
Title string
Image string
Description string
Amazon string
}
func main() {
fmt.Println("HELLO")
showBooks()
}
func showBooks() {
book := Book{
ID: 1,
Title: "Golang",
Author: "Hai",
Description: "Hello",
Amazon: "",
Image: "",
}
// encode to []byte
js, error := json.Marshal(book)
if error != nil {
log.Fatal(error)
}
fmt.Println(js)
// decode bytes
var x Book
json.Unmarshal(js, &x)
fmt.Println(x)
}

Let use json encoder and decoder to parse request and stream response to client. For example, a simple json encoder to write to response

json.NewEncoder(w).Encode(map[string]interface{}{"Result": string(respBytes)})

And simple json decoder to parse request from frontend client

// data struct of request
var request struct {
Query string `json:"query"`
}
// parse user query from request
var query string
error := json.NewDecoder(r.Body).Decode(&request)

And this is full code

func HandleAOSSQueryByTitle(w http.ResponseWriter, r *http.Request, AOSSClient *opensearch.Client, BedrockClient *bedrockruntime.Client) {
// data struct of request
var request struct {
Query string `json:"query"`
}
// parse user query from request
var query string
error := json.NewDecoder(r.Body).Decode(&request)
if error != nil {
fmt.Println(error)
}
query = request.Query
fmt.Println(query)
// query opensearh match by title
response, error := QueryOpenSearchByTitle(AOSSClient, query)
if error != nil {
fmt.Println(error)
}
respBytes, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
// fmt.Println(string(respBytes))
// write answer to response
json.NewEncoder(w).Encode(map[string]interface{}{"Result": string(respBytes)})
}

Interface Method#

Interface is a set of method

// haimtran 07/01/2024
// method on type
// interface as a step of method
package main
import (
"fmt"
)
// method on type
type Abser struct{}
// implement method on type
func (hh Abser) Abs() string {
fmt.Println("abs method implemented")
return "Hello"
}
// a set of method signature
type Demo interface {
SayHello(message string)
SayHi()
}
// method on type
type MyHello struct {}
// implement method 1 on type
func (hh MyHello) SayHello(message string) {
fmt.Printf("say hello %v\n", message)
}
// implement method 2 on type
func (hh MyHello) SayHi() {
fmt.Println("say hi")
}
func main() {
fmt.Println("Hello")
var x Abser
x.Abs()
var a Demo
f := MyHello{}
a = f
a.SayHello("HEHEHE")
a.SayHi()
}

Upload File#

  • create simple frontend form
  • backend handler
  • how to parse the form data

Let create a simple frontend form to upload. There are two options

  • upload using form
  • upload using javascript

Here the script to upload via form data

// formdata
const formData = new FormData()
formData.append('name', 'Hai')
formData.append('file', file)
console.log(formData)
try {
await fetch('/cv-backend', {
method: 'POST',
body: formData
})
} catch (error) {
console.log(error)
}

Here is the detail frontend code

upload.html
<html>
<head>
<title>Upload PDF</title>
<meta name="viewport" content="width=device-width" />
<style>
:root {
box-sizing: border-box;
}
*,
::before,
::after {
box-sizing: inherit;
}
body {
background-color: antiquewhite;
}
.container {
max-width: 500px;
margin: auto;
}
.container-form {
position: relative;
}
.input-question {
width: 100%;
padding: 15px 10px;
}
.button-submit {
background-color: orange;
padding: 10px 25px;
border-radius: 2px;
border: none;
outline: none;
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-50%);
cursor: pointer;
}
.input-file {
width: 100%;
padding: 15px 10px;
background-color: aquamarine;
cursor: pointer;
margin-top: 10px;
}
.container-image {
position: relative;
background-color: gainsboro;
padding: 10px 10px;
align-items: center;
justify-content: center;
display: flex;
max-height: 600px;
height: 50%;
}
.description-image {
position: absolute;
bottom: 0;
left: 0;
background-color: azure;
padding: 10px;
opacity: 0.9;
}
</style>
</head>
<body>
<div class="container">
<div>
<form onkeydown="return event.key != 'Enter';">
<div class="container-form">
<input
type="text"
id="question"
name="question"
class="input-question"
placeholder="what is in this image?"
/>
<button class="button-submit" id="submit" type="submit">
Upload
</button>
</div>
<input type="file" id="file" name="file" class="input-file" />
</form>
</div>
</div>
</body>
<script>
const fileInput = document.getElementById('file')
const submit = document.getElementById('submit')
let file
// get file
fileInput.addEventListener('change', event => {
// get file
file = event.target.files[0]
if (file) {
console.log(file)
}
})
// cal backend cv endpoint
const summaryCV = async () => {
// get user prompt
let question = document.getElementById('question').value
if ((question == '') | (question == null)) {
question = 'what is in this image?'
}
// formdata
const formData = new FormData()
formData.append('name', 'Hai')
formData.append('file', file)
console.log(formData)
try {
await fetch('/cv-backend', {
method: 'POST',
body: formData
})
} catch (error) {
console.log(error)
}
}
submit.addEventListener('click', async event => {
event.preventDefault()
await summaryCV()
})
// listen on enter
document
.getElementById('question')
.addEventListener('keydown', async event => {
if (event.code === 'Enter') {
await summaryCV()
}
})
</script>
</html>

Write a simple backend handler to save the uploaded data

package bedrock
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func HandleCV(w http.ResponseWriter, r *http.Request) {
error := r.ParseMultipartForm(32 << 20)
if error != nil {
fmt.Println(error)
}
// parse parameters
fmt.Println(r.PostForm.Get("name"))
// parse file
file, header, error := r.FormFile("file")
if error != nil {
fmt.Println(error)
}
fmt.Println(header.Filename)
// create file in disk
dest, error := os.Create(header.Filename)
if error != nil {
fmt.Println(error)
}
// write file to disk
io.Copy(dest, file)
json.NewEncoder(w).Encode("hello")
}

Write a simple http server

// handle cv frontend
mux.HandleFunc("/cv", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./static/cv.html")
})
// handle cv backend
mux.HandleFunc("/cv-backend", func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
gobedrock.HandleCV(w, r)
}
})
// allow cors
handler := cors.AllowAll().Handler(mux)
// create a http server using http
server := http.Server{
Addr: ":3000",
Handler: handler,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
MaxHeaderBytes: 1 << 20,
}
server.ListenAndServe()

Full backend code

main.go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
"github.com/rs/cors"
)
func HandleCV(w http.ResponseWriter, r \*http.Request) {
error := r.ParseMultipartForm(32 << 20)
if error != nil {
fmt.Println(error)
}
// parse parameters
fmt.Println(r.PostForm.Get("name"))
// parse file
file, header, error := r.FormFile("file")
if error != nil {
fmt.Println(error)
}
fmt.Println(header.Filename)
// create file in disk
dest, error := os.Create(header.Filename)
if error != nil {
fmt.Println(error)
}
// write file to disk
io.Copy(dest, file)
json.NewEncoder(w).Encode("hello")
}
func main() {
mux := http.NewServeMux()
// handle cv frontend
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "upload.html")
})
// handle cv backend
mux.HandleFunc("/upload-backend", func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
HandleCV(w, r)
}
})
// allow cors
handler := cors.AllowAll().Handler(mux)
// create a http server using http
server := http.Server{
Addr: ":3000",
Handler: handler,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
MaxHeaderBytes: 1 << 20,
}
server.ListenAndServe()
}