can't get Content-Type and Content-Disposition to force browser to display "file->save..." dialog in the web browser

2,111 views
Skip to first unread message

David Marceau

unread,
Jun 23, 2016, 5:35:34 PM6/23/16
to golang-nuts
I'm using TLS/http/gorillamux golang web server.

strSomeFileName := "blah.json" //"blah.txt" // "blah.bin"
w.Header().Set("Content-Type","application/octet-stream")
w.Header().Set("Content-Disposition", "attachment;filename=" + strSomeFileName)
myCmdOutput, _ := cmd.Output()

//with or without following line, it still doesnt display the "file->save..." dialog in the web browser
//w.Header().Set("Content-Length", strconv.Ito( len(myCmdOutput) ) )

w.Write(myCmdOutput)

Does anyone have any hints as to what I'm missing?

Thank you and cheers.

David Marceau

Dave Cheney

unread,
Jun 23, 2016, 6:00:41 PM6/23/16
to golang-nuts
All the examples I find online have a space after attachment; most also quote the filename parameter, filename="foo.txt", although the RFC does not. Hopefully its something as simple as this.

Val

unread,
Jun 23, 2016, 6:15:16 PM6/23/16
to golang-nuts
The commented line seems to have typo strconv.Ito

Maybe the typo prevents proper recompilation, and server goes on with old code?

David Marceau

unread,
Jun 24, 2016, 7:06:59 AM6/24/16
to golang-nuts
Again, I want to clarify the file does arrive in the browser, but I want to ensure the "file->save..." dialog appears in the web browser when it arrives.  I found some older code I wrote a couple of years ago that was behaving as expected:

    w.Header().Set("Content-Type", "application/octet-stream")
    w.Header().Set("Content-Disposition", "attachment; filename=" + myBasePdf + ".pdf")
    http.ServeFile(w, req, myGenPdfFileName)


I acknowledge when I wrote this email I made a typo, but in my code I do have the Itoa correctly.
w.Header().Set("Content-Length", strconv.Itoa( len(myCmdOutput) ) )
I never used that content-length field because I read somewhere that I shouldn't.

Last night I took a look at iris to see how they do it and found:
https://github.com/kataras/iris/blob/master/context.go#L583
err := ctx.ServeFile(filename, false)
if err != nil {
return err
}

ctx.RequestCtx.Response.Header.Set(contentDisposition, "attachment;filename="+destinationName)

I am scratching my head since the header set content-disposition is happening after the ServeFile which is different from what all the docs and what I am used to seeing.  It seems calling these functions are order-independant.  When does the connection actually send the file over the connection?

I believe the Iris send file also provides what I want as expected behaviour, but I haven't tried it yet. 

David Marceau

unread,
Jun 24, 2016, 9:40:29 AM6/24/16
to golang-nuts
I tried to repeat the same ordering as the iris infrastructure within the function, but it still behaves not as expected.  It does not show the file->save... dialog.  It shows the file within the browser as a web page.

func blah (w http.ResponseWriter, r *http.Request) {
    strOutputFileOfJournalctl = "journalctlLog.json"
    w.Header().Set("Content-Type","application/octet-stream")  //forces the save as dialog

    strSomeStringInJsonFormat := "{ Blah: 'blah value' }"
    myOutput := []byte(strSomeStringInJsonFormat)

    //ATTEMPT #1
    //w.Write(myOutput) //displays in web browser page
    //ATTEMPT #4
    //w.Header().Set("Content-Disposition","attachment;filename=" + strOutputFileOfJournalctl)



    //ATTEMPT #3
    //w.Header().Add("Content-Length", strconv.Itoa( len(myOutput) ) )
    //w.Write(myOutput) //displays in web browser page
    //w.Header().Set("Content-Disposition","attachment;filename=" + strOutputFileOfJournalctl)



    //ATTEMPT #2
    w.Header().Add("Content-Length", strconv.Itoa(len(myOutput)) )
    tmpFile, _ := ioutil.TempFile(os.TempDir(), "OurGeneratedCustomLog")
    defer os.Remove(tmpFile.Name())
    tmpFile.Write(myOutput)
    tmpFile.Close()
    http.ServeFile(w, r, tmpFile.Name())
    //ATTEMPT #5
    w.Header().Set("Content-Disposition","attachment;filename=" + "\"" + strOutputFileOfJournalctl + "\"")

David Marceau

unread,
Jun 24, 2016, 9:44:00 AM6/24/16
to golang-nuts
Here is what is in my import.  Maybe I should be looking in goji instead of net/http?

import (
    "fmt"
    "net"
    "time"
    "strconv"
    "strings"
    "os"
    "encoding/json"
    "net/http"
    "crypto/tls"
    "crypto/rand"
    "github.com/gorilla/mux"
    "github.com/goji/httpauth"
    "github.com/zfjagann/golang-ring"
    "github.com/kabukky/httpscerts"
    "reflect"
    "io"
    "io/ioutil"
    "path/filepath"
    "html/template"
    "os/exec"
)

David Marceau

unread,
Jun 24, 2016, 10:57:27 AM6/24/16
to golang-nuts
The core problem was:
w.Header().Set("Content-Disposition","attachment;filename=" + strOutputFileOfJournalctl)
Should actually be:
w.Header().Add("Content-Disposition","attachment;filename=" + strOutputFileOfJournalctl)

I preferred to put the above line before the serveFile.

That's all. Thanks to everyone who dropped by.

Matt Harden

unread,
Jun 25, 2016, 11:41:03 AM6/25/16
to David Marceau, golang-nuts
That's very strange. Why didn't Set work? Was there some already-existing Content-Disposition value in the header that you needed to retain?

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

akira....@gmail.com

unread,
Jul 26, 2016, 7:23:56 AM7/26/16
to golang-nuts
Hello, I'm facing a very similar problem but I still cannot solve it by your solution. Would you mind help me ? Thank you very much
Reply all
Reply to author
Forward
0 new messages