Golang transmit files over a net/http server to clients
In this example I'll show you how to send a file from a net/http listener to a client.
Update: resume functionality!
Check the updated version of this post.
The example
The example starts a web-server at port 80, you can access it locally in your browser by "http://localhost/" or "127.0.01".
If you want to test the example put some dummy files in the same folder as the compiled binary.
We send our request with a GET parameter in the URL, the key is 'file' and the value should be the file you want to download.
E.g.
http://127.0.0.1/?file=IWantToDownloadThisFile.zip
The code
Everything is explained in the comments.
package main
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
)
func main() {
fmt.Println("Starting http file sever")
http.HandleFunc("/", HandleClient)
err := http.ListenAndServe(":80", nil)
if err != nil {
fmt.Println(err)
}
}
func HandleClient(writer http.ResponseWriter, request *http.Request) {
//First of check if Get is set in the URL
Filename := request.URL.Query().Get("file")
if Filename == "" {
//Get not set, send a 400 bad request
http.Error(writer, "Get 'file' not specified in url.", 400)
return
}
fmt.Println("Client requests: " + Filename)
//Check if file exists and open
Openfile, err := os.Open(Filename)
defer Openfile.Close() //Close after function return
if err != nil {
//File not found, send 404
http.Error(writer, "File not found.", 404)
return
}
//File is found, create and send the correct headers
//Get the Content-Type of the file
//Create a buffer to store the header of the file in
FileHeader := make([]byte, 512)
//Copy the headers into the FileHeader buffer
Openfile.Read(FileHeader)
//Get content type of file
FileContentType := http.DetectContentType(FileHeader)
//Get the file size
FileStat, _ := Openfile.Stat() //Get info from file
FileSize := strconv.FormatInt(FileStat.Size(), 10) //Get file size as a string
//Send the headers
writer.Header().Set("Content-Disposition", "attachment; filename="+Filename)
writer.Header().Set("Content-Type", FileContentType)
writer.Header().Set("Content-Length", FileSize)
//Send the file
//We read 512 bytes from the file already, so we reset the offset back to 0
Openfile.Seek(0, 0)
io.Copy(writer, Openfile) //'Copy' the file to the client
return
}
Do I have to send headers?
Yes, at the very least "Content-Disposition". In some older browsers if you don't specify a binary file as "attachment;" it will print out the binary content on the screen instead of saving it to the hard disk.
Specifying "attachment;" on an image or picture will make the browser download the file instead of showing it on the screen.
Also if you don't specify the "filename", chrome for example, would save the file as "download".
According the w3 HTTP protocol
1 you must declare the "Content-Type".Any HTTP/1.1 message containing an entity-body SHOULD include a Content-Type header field defining the media type of that body. If and only if the media type is not given by a Content-Type field, the recipient MAY attempt to guess the media type via inspection of its content and/or the name extension(s) of the URI used to identify the resource. If the media type remains unknown, the recipient SHOULD treat it as type "application/octet-stream".
If you don't pass the "Content-Length" header, the clients won't be able to see the progress of the download, and the browser will keep reading until the server closes connection, and in this case the browser wont be able to tell if the file was completely transmitted.