Preface:
Recently, I have completed a formal web application using Go language. There are some problems that are more important in the process of developing web applications using Go. In the past, I have taken web development as a career and used different languages and paradigms to develop web applications as a hobby, so I have some experience in the field of web development.
Overall, I like to use Go for web development, although I need to get used to it for a while. There are some pitfalls in Go, but as the file upload and download discussed in this article, the standard library and built-in functions of Go make development a pleasant experience.
In the next few posts, I will focus on some of the issues I encountered when writing production-grade web applications in Go, especially with regard to authentication/authorization.
This article will show you basic examples of HTTP file upload and download. We take an HTML form with a type text box and a uploadFile upload box as the client.
Let's take a look at how the Go language solves this problem that is everywhere in web development.
Code example First, we set two routes on the server side, /upload is used for file upload and /files/* is used for file download.
const maxUploadSize = 2 * 1024 * 2014 // 2 MB const uploadPath = "./tmp" func main() { ("/upload", uploadFileHandler()) fs := ((uploadPath)) ("/files/", ("/files", fs)) ("Server started on localhost:8080, use /upload for uploading files and /files/{fileName} for downloading files.") ((":8080", nil)) }
We also define the target directory we want to upload, and the maximum file size we accept as constants. Note that the concept of the entire file service is so simple here - we only use tools from the standard library to create an HTTP handler, which will use the directory provided by (uploadPath) to upload files.
Now we just need to implement uploadFileHandler .
This handler will contain the following functions:
- Verify file maximum value
- Verify file and POST parameters from request
- Check the file type provided (we only accept images and PDFs)
- Create a random file name
- Write files to hard disk
- Handle all errors and return the success message if everything goes well
In the first step, we define the handler:
func uploadFileHandler() { return (func(w , r *) {
We then use verified file size, which returns an error when the file size is larger than the set value. The error will be processed by a helper program renderError, which returns the error message and the corresponding HTTP status code.
= (w, , maxUploadSize) if err := (maxUploadSize); err != nil { renderError(w, "FILE_TOO_BIG", ) return }
If the file size verification passes, we will check and parse the form parameter type and uploaded file and read the file. In this example, for clarity, we do not use fancy and whistle interfaces, we simply read the file into a byte array, which we will write later.
fileType := ("type") file, _, err := ("uploadFile") if err != nil { renderError(w, "INVALID_FILE", ) return } defer () fileBytes, err := (file) if err != nil { renderError(w, "INVALID_FILE", ) return }
Now that we have successfully verified the file size and read the file, it is time to check the file type. A cheap but not safe way to just check the file extension and believe that the user has not changed it, but it shouldn't be done for a formal project.
Fortunately, the Go standard library provides us with a function that is based on the mimesniff algorithm and can determine the file type only by reading the first 512 bytes of the file.
filetype := (fileBytes) if filetype != "image/jpeg" && filetype != "image/jpg" && filetype != "image/gif" && filetype != "image/png" && filetype != "application/pdf" { renderError(w, "INVALID_FILE_TYPE", ) return }
In a real application, we might do things with file metadata, such as saving it to a database or pushing it to an external service—in any way we will parse and manipulate the metadata. Here we create a random new name (which may be a UUID in practice) and record the new file name.
fileName := randToken(12) fileEndings, err := (fileType) if err != nil { renderError(w, "CANT_READ_FILE_TYPE", ) return } newPath := (uploadPath, fileName+fileEndings[0]) ("FileType: %s, File: %s\n", fileType, newPath)
The task is done immediately, and there is only one key step left - writing a file. As mentioned above, we only need to copy the read binary file into a newly created file handler named newFile.
If all parts are OK, we return a SUCCESS message to the user.
newFile, err := (newPath) if err != nil { renderError(w, "CANT_WRITE_FILE", ) return } defer () if _, err := (fileBytes); err != nil { renderError(w, "CANT_WRITE_FILE", ) return } ([]byte("SUCCESS"))
This is OK. You can test this simple example by uploading HTML pages, cURLs, or tools such as postman [1] using a virtual file.
Here is the complete code example here [2]
Conclusion This is another demonstration of how Go allows users to write simple and powerful software for the web without having to deal with countless layers of abstraction inherent in other languages and ecosystems.
In the next section, I will show you some other details in my first time writing a formal web application in Go, so stay tuned. ;)
// Some codes have been modified based on feedback from reddit user lstokeworth. Thanks:)
This is the article about the upload and download of HTTP files in Go language. For more related content on uploading Go HTTP files, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!