2

I'm using Excelize library for generating xlsx documents. When I use It's Write(io.writer) func for saving xlsx to file, it works perfectly. But I need to generate and serve this file at web-server. I was trying this solution

func GetConsolidatedReport(w http.ResponseWriter, r *http.Request) {
    var reportFile *excelize.File

    ...

    var b bytes.Buffer
    writr := bufio.NewWriter(&b)
    reportFile.SaveAs("/tmp/testfile.xlsx")
    reportFile.Write(writr)
    writr.Flush()

    fileContents := b.Bytes()
    fileSize := strconv.Itoa(len(fileContents))

    w.Header().Set("Content-Disposition", "attachment; filename=report.xlsx")
    w.Header().Set("Content-Type", "application/octet-stream")
    w.Header().Set("Content-Length", fileSize)

    t := bytes.NewReader(b.Bytes())
    io.Copy(w, t)
}

and after that i've got corrupted zip-file from web-server, but normal file saved in "/tmp/testfile.xlsx"

I've tried content-type as application/zip, application/octet-stream, application/vnd.* but no luck.

Can you help me? Thank you in advance.

PS: By serving i mean on-the-fly generation of file, sorry for any misunderstanding.

PS2: It seems I get an overhead of downloaded file (8085 bytes of original and 13000+ of downloaded) and I can't figure it out where this overhead is come in.

Eugene Lisitsky
  • 12,113
  • 5
  • 38
  • 59
TheROX
  • 96
  • 13
  • I dont know how you are using the front, but in a project I had this type of error because the download started before the finish of writing the file on the server. – danilo Jan 11 '18 at 14:11
  • He used a flush so I think the writing is done. – leaf bebop Jan 11 '18 at 14:19
  • Is the call to `SaveAs` necessary since you're not actually trying to write to disk? Also the correct MIME content type for xlsx is `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`. – Adrian Jan 11 '18 at 14:37
  • @Adrian no, it is absolutly unnecessary, I used it to get valid file to check it worked. Thank you for the correct MIME. – TheROX Jan 11 '18 at 14:41
  • Can you write the bytes to a file to compare it to original file? And in case you didn't realize, `ServeContent` serves a file **from memory**. – leaf bebop Jan 11 '18 at 15:04
  • @leafbebop ... fileSize := strconv.Itoa(len(fileContents)) ff, _ := os.Create("/tmp/222.xlsx") ff.Write(fileContents) defer ff.Close() w.Header().Set("Content-Disposition", "attachment; filename=report.xlsx") ... This code gives me two working files same size. ServeContent returns corrupted file too. – TheROX Jan 11 '18 at 15:13
  • And did the corrupted file have anything similar to the original one? Try use a hex dump to analyse what it contains. – leaf bebop Jan 11 '18 at 16:00
  • 1
    The code `t := bytes.NewReader(b.Bytes()); io.Copy(w, t)` can be simplified to `w.Write(b.Bytes())`. – Charlie Tumahai Jan 11 '18 at 19:39
  • 1
    Almost four years later, I stumbled across the same issue. Did you find a solution by any chance? – Teecup Oct 08 '21 at 16:57
  • I had the same problem using "Advanced Rest Client" (a chrome extension for sending requests). try using chrome itself (for GET request), postman and other tools. – faza Aug 20 '22 at 11:25

2 Answers2

2

For serving a reader, you should consider http.ServeContent. It will handle the header, content range and so on for you. Change the line withio.Copy with http.ServeContent(w,r,"testfile.xlsx",time.Now(),t) and it shall work. Documents: https://golang.org/pkg/net/http/#ServeContent

leaf bebop
  • 7,643
  • 2
  • 17
  • 27
  • I don't want to serve file from disk. I want to generate file on the fly and send it to client. I use `SaveAs` function just to make sure that file is valid. – TheROX Jan 11 '18 at 14:22
  • 3
    It indeed serve file from memory as it takes a readseeker. @therox – leaf bebop Jan 11 '18 at 14:35
1

i use *File.write api and it works:

package main

import (
    "net/http"

    "github.com/xuri/excelize/v2"
)

func main() {

    s := http.Server{
        Addr: ":8012",
    }

    http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
        f := excelize.NewFile()
        // Set value of a cell.
        f.SetCellValue("Sheet1", "B2", 100)
        w.Header().Set("Content-Type", "application/octet-stream")
        w.Header().Set("Content-Disposition", "attachment; filename=example.xlsx")
        w.Header().Set("Content-Transfer-Encoding", "binary")
        f.Write(w)
    })

    s.ListenAndServe()
}

zizhen zhan
  • 200
  • 3
  • 6