I'm just getting started learning about html/templates in Go. The error I'm getting is that 'system cannot find file path specified'. and the file path is templates/time.html. the location of time.html (the page I'm trying to render) is
src/templates/time.html
the location of my go main is src/timeserver/timerserver.go
here's the code I used
func TimeServer(w http.ResponseWriter, req *http.Request) {
// if user goes to another website after time/...
if req.URL.Path != "/time/" {
errorHandler(w, req, http.StatusNotFound)
return
}
cookie, _ := req.Cookie("UUID")
//existCheck := false
//temp2 := ""
profile := Profile{"",time.Now().Format("3:04:04 PM")}
if cookie != nil { // if cookie exist set flags
name, check := cookieJar.GetValue(cookie.Value)
profile = Profile{name,time.Now().Format("3:04:04 PM")}
fmt.Println(name)
//existCheck = check
//temp2 = name
fmt.Println(check)
}
fp := path.Join("templates", "time.html")
tmpl, err := template.ParseFiles(fp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tmpl.Execute(w, profile); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
Problem was that my path wasn't correct. changed
fp := path.Join("templates", "time.html")
to
fp := path.Join("Home/go/src/templates", "time.html")
Related
Highlevel:my program gets a long URL and makes it shorter(like tinyurl). But i have problem passing Long URL variable and shortURL variable from function to a mySQL database. I have tried looking it up on internet but noluck and information im getting is not close to what i have. im only posting some parts of my code the full code will be in playground
var db *sql.DB //global variable
func main(){
var db, err = sql.Open("mysql", dsn())
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Printf("Connection established to MYSQL server\n%s\n", dsn())
}
defer db.Close()
linkList = map[string]string{}
http.HandleFunc("/link", addLink)
http.HandleFunc("/hpe/", getLink)
http.HandleFunc("/", Home)
// Flags to pass string ip or port to WEB app
ip := flag.String("i", "0.0.0.0", "")
port := flag.String("p", "8080", "")
flag.Parse()
fmt.Printf("Web application listening on %s \n", net.JoinHostPort(*ip, *port))
log.Fatal(http.ListenAndServe(net.JoinHostPort(*ip, *port), nil))
}
function that creates short url. Every time this func is called it produces link
func addLink(w http.ResponseWriter, r *http.Request) {
log.Println("Add Link")
key, ok := r.URL.Query()["ik"]
if ok {
if !validLink(key[0]) {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Could not create shortlink need absolute path link.")
return
}
log.Println(key)
if _, ok := linkList[key[0]]; !ok {
genString := randomString(5)
linkList[genString] = key[0]
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusAccepted)
linkString := fmt.Sprintf("hpe/%s", genString, genString)
fmt.Fprintf(w, "Added shortlink\n")
fmt.Fprintf(w, linkString)
return
// database function
defer db.Close()
s:
result, err := db.Exec("insert into Url (LongUrl, ShortUrl) value(?,?);",genString,linkString)
if err != nil {
fmt.Print(err.Error())
} else {
_,err:=result.LastInsertId()
}
}
w.WriteHeader(http.StatusConflict)
fmt.Fprintf(w, "Already have this link")
return
}
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Failed to add link")
return
}
func getLink(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
log.Println("Get Link:", path)
pathArgs := strings.Split(path, "/")
if len(pathArgs[2]) < 1 {
w.WriteHeader(http.StatusNotFound)
http.Redirect(w, r, "0.0.0.0:8080", http.StatusTemporaryRedirect)
return
}
log.Printf("Redirected to: %s", linkList[pathArgs[2]])
http.Redirect(w, r, linkList[pathArgs[2]], http.StatusTemporaryRedirect)
//fmt.Printf("all %s", linkList)
return
}
My expectation is when func addlink gets called the info that generated from long and short url gets put into database like in the code.
Building a backend go server that can take a form with multiple inputs and 3 of them have multiple file inputs. I searched and it states that if you want to make something like this work you don't want to use the typical
if err := r.ParseMultipartForm(32 << 20); err != nil {
fmt.Println(err)
}
// get a reference to the fileHeaders
files := r.MultipartForm.File["coverArt"]
and instead you should use
mr, err := r.MultipartReader()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
Standard form-data:
Name
Email
Cover art photos (multiple files)
Profile photos (multiple files)
2 Audio files (2 songs)
2 Videos (personal intro, recording of person in a cappella)
HTML Form
<form method="post" enctype="multipart/form-data" action="/upload">
<input type="text" name="name">
<input type="text" name="email">
<input name="coverArt" type="file" multiple />
<input name="profile" type="file" multiple />
<input type="file" name="songs" multiple />
<input type="file" name="videos" multiple/>
<button type="submit">Upload File</button>
</form>
Go Code:
func FilePOST(w http.ResponseWriter, r *http.Request) error {
fmt.Println("File Upload Endpoint Hit")
mr, err := r.MultipartReader()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
for {
part, err := mr.NextPart()
// This is OK, no more parts
if err == io.EOF {
break
}
// Some error
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
// CoverArt 'files' part
if part.FormName() == "coverArt" {
name := part.FileName()
outfile, err := os.Create("uploads/" + name)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
// return
}
defer outfile.Close()
_, err = io.Copy(outfile, part)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
// return
}
}
// Profile Pic 'files' part
if part.FormName() == "profile" {
name := part.FileName()
outfile, err := os.Create("uploads/" + name)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
// return
}
defer outfile.Close()
_, err = io.Copy(outfile, part)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
// return
}
}
// Songs 'files' part
if part.FormName() == "songs" {
name := part.FileName()
outfile, err := os.Create("uploads/" + name)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
// return
}
defer outfile.Close()
_, err = io.Copy(outfile, part)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
// return
}
}
// Video 'files' part
if part.FormName() == "videos" {
name := part.FileName()
outfile, err := os.Create("uploads/" + name)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
// return
}
defer outfile.Close()
_, err = io.Copy(outfile, part)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
// return
}
}
}
fmt.Println("done")
return nil
}
Go Server Error:
go run main.go [15:58:21]
now serving at the following location www.localhost:3000
File Upload Endpoint Hit
INFO[0009] POST /upload elapsed="680.422µs" host= method=POST path=/upload query=
2021/07/14 15:58:32 http: panic serving [::1]:62924: runtime error: invalid memory address or nil pointer dereference
It is hard to guess where your code panics. Probably the reason is that your program continue to execute when error occurs. For example if creation of file fails, outfile.Close() will panic as the outfile is nil.
Both approaches support multiple files for single field. The difference is in how they handle memory. The streaming version reads small portions of data from the network and writes it to a file when you call io.Copy. The other variant loads all the data into memory when you call ParseMultiForm(), so it requires as much memory as the size of the files you want to transfer. Below you will find working examples for both variants.
Streaming variant:
func storeFile(part *multipart.Part) error {
name := part.FileName()
outfile, err := os.Create("uploads/" + name)
if err != nil {
return err
}
defer outfile.Close()
_, err = io.Copy(outfile, part)
if err != nil {
return err
}
return nil
}
func filePOST(w http.ResponseWriter, r *http.Request) error {
fmt.Println("File Upload Endpoint Hit")
mr, err := r.MultipartReader()
if err != nil {
return err
}
for {
part, err := mr.NextPart()
// This is OK, no more parts
switch {
case errors.Is(err, io.EOF):
fmt.Println("done")
return nil
case err != nil:
// Some error
return err
default:
switch part.FormName() {
case "coverArt", "profile", "songs", "videos":
if err := storeFile(part); err != nil {
return err
}
}
}
}
}
func main() {
http.HandleFunc("/upload", func(writer http.ResponseWriter, request *http.Request) {
err := filePOST(writer, request)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
log.Println("Error", err)
}
})
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
And version with ParseMultipartForm, which reads data to memory.
func storeFile(part *multipart.FileHeader) error {
name := part.Filename
infile, err := part.Open()
if err != nil {
return err
}
defer infile.Close()
outfile, err := os.Create("uploads/" + name)
if err != nil {
return err
}
defer outfile.Close()
_, err = io.Copy(outfile, infile)
if err != nil {
return err
}
return nil
}
func FilePOST(w http.ResponseWriter, r *http.Request) error {
fmt.Println("File Upload Endpoint Hit")
if err := r.ParseMultipartForm(2 << 24); err != nil {
return err
}
for _, fileType := range []string{"coverArt", "profile", "songs", "videos"} {
uploadedFiles, exists := r.MultipartForm.File[fileType]
if !exists {
continue
}
for _, file := range uploadedFiles {
if err := storeFile(file); err != nil {
return err
}
}
}
return nil
}
func main() {
http.HandleFunc("/upload", func(writer http.ResponseWriter, request *http.Request) {
err := FilePOST(writer, request)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
log.Println("Error", err)
}
})
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
I have a gin application that receives a post request containing a csv file which I want to read without saving it. I'm stuck here trying to read from the post request with the following error message: cannot use file (variable of type *multipart.FileHeader) as io.Reader value in argument to csv.NewReader: missing method Read
file, err := c.FormFile("file")
if err != nil {
errList["Invalid_body"] = "Unable to get request"
c.JSON(http.StatusUnprocessableEntity, gin.H{
"status": http.StatusUnprocessableEntity,
"error": errList,
})
}
r := csv.NewReader(file) // <= Error message
records, err := r.ReadAll()
for _, record := range records {
fmt.Println(record)
}
Is there a good example that I could use?
first read the file and header
csvPartFile, csvHeader, openErr := r.FormFile("file")
if openErr != nil {
// handle error
}
then read the lines from the file
csvLines, readErr := csv.NewReader(csvPartFile).ReadAll()
if readErr != nil {
//handle error
}
you can go through the lines looping through the records
for _, line := range csvLines {
fmt.Println(line)
}
As other answers have mentioned, you should Open() it first.
The latest version of gin.Context.FromFile(string) seems to return only two values.
This worked for me:
func (c *gin.Context) {
file_ptr, err := c.FormFile("file")
if err != nil {
log.Println(err.Error())
c.Status(http.StatusUnprocessableEntity)
return
}
log.Println(file_ptr.Filename)
file, err := file_ptr.Open()
if err != nil {
log.Println(err.Error())
c.Status(http.StatusUnprocessableEntity)
return
}
defer file.Close()
records, err := csv.NewReader(file).ReadAll()
if err != nil {
log.Println(err.Error())
c.Status(http.StatusUnprocessableEntity)
return
}
for _, line := range records {
fmt.Println(line)
}
}
I found a few similar questions to this one, but their implementations are not working for me and I'm wondering if it's something about my test environment or if I'm just doing something incorrectly.
Here's how I'm attaching the file in my test request:
file := new(bytes.Buffer)
f, err := os.Open("myfile.csv")
if err != nil {
t.Fatal(err)
}
defer f.Close()
io.Copy(f, file)
req, err := http.NewRequest("POST", "/upload", file)
I got an error: "http: no such file"
When I get to the line that has file, _, err := r.FormFile("file") in the controller.
I also wondered if "file" is the wrong key for FormFile so I also tried "fileupload" and it's the same. A little confused on that.
So I also tried to just build the "csv" on the fly with a csv.NewWriter and it came to a similar result.
Again, the route works great with a postman request that is just about identical, but for some reason I can't get it working for my test.
Update: here is more of the test for clarity on how I serve the request. Thanks to Flimzy's comment, I have removed io.Copy and I've now progressed to a new error as well:
f, err := os.Open("myfile.csv")
if err != nil {
t.Fatal(err)
}
defer f.Close()
mw := multipart.NewWriter(f)
mw.Close()
req, err := http.NewRequest("POST", "/upload", f)
req.Header.Add("Content-Type", mw.FormDataContentType())
// add the discountID URL var
req = mux.SetURLVars(req, map[string]string{
"discountID": discountID,
})
rr := httptest.NewRecorder()
controller := generateDiscountsController()
// Set middleware memberInfo context
gContext.Set(req, "memberInfo", memberInfo)
// Pass appropriate controller method to http handler in this case
// Get discount
handler := http.HandlerFunc(controller.ImportCSV)
handler.ServeHTTP(rr, req)
resp := response.GetBodyMessage(rr.Body)
assert.Equal(t, 201, rr.Code)
assert.Equal(t, "success", resp)
error: "multipart: NextPart: EOF" this one catches earlier in my controller on err := r.ParseMultipartForm(32 << 20)
In my controller the request is handled here:
func (d *APIDiscount) ImportCSV(w http.ResponseWriter, r *http.Request) {
var buf bytes.Buffer
var firstTen [][]string
var columns []string
defer buf.Reset()
vars := mux.Vars(r)
// using csv writer is safer and better at catching errors than io.Copy
csvData := csv.NewWriter(&buf)
memberInfo := gContext.Get(r, "memberInfo").(*entity.MemberInfo)
err := r.ParseMultipartForm(32 << 20) // max 32 MB <-- Catching here in the test, but not when I send the request through postman on my local server with the same file
if err != nil {
response.InternalError.Send(w, r, memberInfo, 400)
}
file, _, err := r.FormFile("file") // <-- Used to catch here when I used io.Copy
if err != nil {
if err == http.ErrMissingFile {
response.MissingFile.Send(w, r, memberInfo, 400)
return
}
response.FileNotAccepted.Send(w, r, memberInfo, 400)
return
}
// ...
I build a client and a server in golang both are using this functions to encrypt/decrypt
func encrypt(text []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
b := base64.StdEncoding.EncodeToString(text)
ciphertext := make([]byte, aes.BlockSize+len(b))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
cfb := cipher.NewCFBEncrypter(block, iv)
cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
return ciphertext, nil
}
func decrypt(text []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if len(text) < aes.BlockSize {
return nil, errors.New("ciphertext too short")
}
iv := text[:aes.BlockSize]
text = text[aes.BlockSize:]
cfb := cipher.NewCFBDecrypter(block, iv)
cfb.XORKeyStream(text, text)
data, err := base64.StdEncoding.DecodeString(string(text))
if err != nil {
return nil, err
}
return data, nil
}
so yeah I make a normal post request
url := "https://"+configuration.Server+configuration.Port+"/get"
// TODO maybe bugs rest here
ciphertext, err := encrypt([]byte(*getUrl))
if err != nil {
fmt.Println("Error: " + err.Error())
}
fmt.Println(string(ciphertext))
values := map[string]interface{}{"url": *getUrl, "urlCrypted": ciphertext}
jsonValue, _ := json.Marshal(values)
jsonStr := bytes.NewBuffer(jsonValue)
req, err := http.NewRequest("POST", url, jsonStr)
and the servercode is as following
requestContent := getRequestContentFromRequest(req)
url := requestContent["url"].(string)
undecryptedUrl := requestContent["urlCrypted"].(string)
decryptedurl, err := decrypt([]byte(undecryptedUrl))
if err != nil {
fmt.Println("Error: " + err.Error())
}
fmt.Println(decryptedurl)
where getRequestContentFromRequest is as following
func getRequestContentFromRequest(req *http.Request)
map[string]interface{} {
buf := new(bytes.Buffer)
buf.ReadFrom(req.Body)
data := buf.Bytes()
var requestContent map[string]interface{}
err := json.Unmarshal(data, &requestContent)
if err != nil {
fmt.Println(err)
}
return requestContent
}
Now to the problem.
If I encrypt my string in the client and decrypt it direct after that everything is fine.
But, when I send the encrypted string to the server and try to decrypt it with literrally the same function as in the client, the decrypt function throws an error.
Error: illegal base64 data at input byte 0
I think the Problem is the unmarshalling of the JSON.
Thanks for help.
P.S.
Repos are
github.com/BelphegorPrime/goSafeClient and github.com/BelphegorPrime/goSafe
UPDATE
Example JSON
{"url":"facebook2.com","urlCrypted":"/}\ufffd\ufffd\ufffdgP\ufffdN뼞\ufffd\u0016\ufffd)\ufffd\ufffd\ufffdy\u001c\u000f\ufffd\ufffd\ufffdep\ufffd\rY\ufffd\ufffd$\ufffd\ufffd"}
UPDATE2
I made a playground here
The problem is that you encode in base64 twice. The first time in the encrypt function and the second time during the JSON marshalling. byte slices are automatically converted into base64 strings by the encoding/json marshaller.
The solution is to decode the base64 string before calling decrypt.
Example on the Go PlayGround
EDIT
Working solution here