Uploading files
Uploading files is the next step in form processing, in case of files, we send the entire file data in the HTTP header, so we have to set the form
encoding to enctype="multipart/form-data"
. This will inform our server that we are going to get a file from the form along with the rest of the
fields, if any.
This means we can get either either file(s) and data or just file(s) or just data and no file(s).
At the first line of our HTTP handler, we have to write this line, if this line is not present in the first line then it gives unexpected results
file, handler, err := r.FormFile("uploadfile")
if handler != nil {
r.ParseMultipartForm(32 << 20) //defined maximum size of file
defer file.Close()
f, err := os.OpenFile("./files/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
log.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
filelink := "<br> <a href=./files/"+handler.Filename+">"+ handler.Filename+"</a>"
content = content + filelink
}
We first provide the maximum size of the file which is 32 ^ 20, which is gigantic for our webapp, not everyone has the Internet infrastructure to upload that big a file, but we want to be flexible.
We basically get a file from the form request, in the form handler we open another file with the same/different name and then read the file form the request and write it on the server. We need to handle the scene where we name the file differently so we'd need to store the old file name -> new file name relation somewhere, it can be a database table.
The file name should be scrubbed, since the user can give any malicious name to damage our application.
We now want to randomize the file name of the files which the users upload. In your AddTaskFunc
add the following lines
if handler != nil {
r.ParseMultipartForm(32 << 20) //defined maximum size of file
defer file.Close()
randomFileName := md5.New()
io.WriteString(randomFileName, strconv.FormatInt(time.Now().Unix(), 10))
io.WriteString(randomFileName, handler.Filename)
token := fmt.Sprintf("%x", randomFileName.Sum(nil))
f, err := os.OpenFile("./files/"+token, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
log.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
filelink := "<br> <a href=/files/" + token + ">" + handler.Filename + "</a>"
content = content + filelink
fileTruth := db.AddFile(handler.Filename, token)
if fileTruth != nil {
message = "Error adding filename in db"
log.Println("error adding task to db")
}
}
file ~/Tasks/db/db.go
// AddFile is used to add the md5 of a file name which is uploaded to our application
// this will enable us to randomize the URL without worrying about the file names
func AddFile(fileName, token string) error {
SQL, err := database.Prepare("insert into files values(?,?)")
if err != nil {
log.Println(err)
}
tx, err := database.Begin()
if err != nil {
log.Println(err)
}
_, err = tx.Stmt(SQL).Exec(fileName, token)
if err != nil {
log.Println(err)
tx.Rollback()
} else {
log.Println(tx.Commit())
}
return err
}
table structure
CREATE TABLE files(name varchar(1000) not null, autoName varchar(255) not null);
These block of code do the following things:
- Create a version of name for each uploaded file
- Insert it in database
- Reference the file as
/files/<randomName>
- File is now referenced by our name rather than the user supplied name
The next part to do is registering the /files/
handler.
file: ~/Tasks/views/views.go
// UploadedFileHandler is used to handle the uploaded file related requests
func UploadedFileHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
log.Println("into the handler")
token := r.URL.Path[len("/files/"):]
//file, err := db.GetFileName(token)
//if err != nil {
log.Println("serving file ./files/" + token)
http.ServeFile(w, r, "./files/"+token)
//}
}
}