Renátó Bogár
Renato's blog

Renato's blog

Better way to share content with client in Go

Better way to share content with client in Go

Renátó Bogár's photo
Renátó Bogár
·May 8, 2022·

3 min read

Subscribe to my newsletter and never miss my upcoming articles

When sharing information from your server to the client, there are many ways to do it in Go. Lets imagine a scenario when you make a server side rendered website. When you enter the page, all the static contents those are required for the given path of the application, are downloaded. For example these contents can be an HTML file, a CSS file, a Js file etc.

Now imagine a scenario when you want to share data from you server which has significally larger size. For example a video. Depending on the quality and the length of it, a video can easily be very large: hundred or thousands of MB. According the my previous post you can write your own handlers for a given path, so lets write one.

func (this *MyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    path := "public" + req.URL.Path
    data, err := ioutil.ReadFile(string(path))
    if err == nil {
        var contentType string
        if strings.HasSuffix(path, ".css") {
            contentType = "text/css"
        } else if strings.HasSuffix(path, ".html") {
            contentType = "text/html"
        } else if strings.HasSuffix(path, ".js") {
            contentType = "application/javascript"
        } else if strings.HasSuffix(path, ".png") {
            contentType = "image/png"
        } else {
            contentType = "text/plain"
        }

        w.Header().Add("Content Type", contentType)
        w.Write(data)
    } else {
        w.WriteHeader(404)
        w.Write([]byte("404 - " + http.StatusText(404)))
    }
}

All this code snippet does is to provide different kind of contents to the client, depending on the URL path.

Now lets extend this function with an additional else if

else if strings.HasSuffix(path, ".mp4") { 
        contentType = "video/mp4"
}

In this case if the user enters the URL that matches a video name, it becomes playable on the client side.

It does work, but what is the problem with it ?

The problem is the following line:

data, err := ioutil.ReadFile(string(path))

What this line of code does, is it stores the data in the RAM. Now if you have a video that is like 500MB (which is not that long video in a good quality) it gets stored right in the RAM. Now this way you can not serve hundreds or thousands of requests that easily.

There are other solutions for problems like this, but I would like to mention the one I used. I changed ioutil package to another which is called bufio.

With bufio the function above looks like this.

func (this *MyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    path := "public" + req.URL.Path
    f, err := os.Open(path)

    if err == nil {
        var bufferedReader = bufio.NewReader(f)

        var contentType string
        if strings.HasSuffix(path, ".css") {
            contentType = "text/css"
        } else if strings.HasSuffix(path, ".html") {
            contentType = "text/html"
        } else if strings.HasSuffix(path, ".js") {
            contentType = "application/javascript"
        } else if strings.HasSuffix(path, ".png") {
            contentType = "image/png"
        } else if strings.HasSuffix(path, ".mp4") { 
            contentType = "video/mp4"
        } else {
            contentType = "text/plain"
        }

        w.Header().Add("Content Type", contentType)
        bufferedReader.WriteTo(w)
    } else {
        w.WriteHeader(404)
        w.Write([]byte("404 - " + http.StatusText(404)))
    }
}

Using the function with this package, my RAM demand became a hundred time less, compared to using ioutil.

I hope this post will be useful you, thank you for reading!

Did you find this article valuable?

Support Renátó Bogár by becoming a sponsor. Any amount is appreciated!

See recent sponsors Learn more about Hashnode Sponsors
 
Share this