0
votes

I am trying to make a REST API using gin-gonic in golang, which takes some path parameters from a GET request & downloads that file.

That works correctly by curl command. However, when I add swagger UI documentation, then if the downloaded file is text or image, it is just displayed in that webpage, but I can't see any download option. Further the browser hangs if I enter the path of a video file.

Also, the MIME type of the file to be downloaded is determined correctly from gin-gonic. But, to download the file via swagger UI interface, I am using hardcoded @Accept & @Produce swagger UI comment annotations. It should ideally determine that MIME type automatically.

Currently the response type is listed as drop down, where I get various options. I need that to automatically detect correct MIME type & display a download button if possible

My code for the GET request is:

// DownloadObject godoc
// @Summary Download object
// @Description Download object
// @Produce application/json
// @Produce text/plain
// @Produce application/octet-stream
// @Produce video/mp4
// @Produce image/png
// @Produce image/jpeg
// @Produce image/gif
// @Param rootPath path string true "Root Path"
// @Param filePath path string true "File Path"
// @Header 200 {string} Token "qwerty"
// @Router /transfer/{rootPath}/{filePath} [get]
func DownloadObject(c *gin.Context) {

    // Get rootPath & filePath from GET request
    rootPath := c.Param("rootPath")
    filePath := c.Param("filePath")


    // Some code to get an io.Reader object "download"

    _, err = io.Copy(c.Writer, download)
    if err != nil {
        c.Status(http.StatusInternalServerError)
        log.Print(err)
        return
    }

    err = download.Close()
    if err != nil {
        c.Status(http.StatusInternalServerError)
        log.Print(err)
        return
    }
    
    c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%", filePath))
    c.Writer.Header().Add("Content-Type", c.GetHeader("Content-Type"))
    // Object download completed & closed successfully

}

Am I missing something here?

1
You're adding your headers after the file has been received by the browser, move them before io.Copy to have an effect. Also you have a wrong formatting verb (just %) in the fmt.Sprintf call, use %q. Not sure about the restxarantolus
@xarantolus Thanks a ton friend! It works! :) Now, I see the "Download file" link. Please post the same as an answer, so that I may accept that :) One more thing: Is the file download started before I click on "Download file" link? I am not sure if download starts after clicking on the link. Kindly help, Thanks againuser14200418
the download should only start after you clicked the button, but it really depends on how it is implemented (e.g. there could be logic that starts it on hover). The request should only come in after you clickedxarantolus

1 Answers

1
votes

You're adding headers after the file has been received by the browser, move them before io.Copy for them to have an effect. Also you have a wrong formatting verb (just %) in the fmt.Sprintf call, use %q:

func DownloadObject(c *gin.Context) {

    // Get rootPath & filePath from GET request
    rootPath := c.Param("rootPath")
    filePath := c.Param("filePath")

    // ...

    // Add headers to the response
    c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filePath))
    c.Writer.Header().Add("Content-Type", c.GetHeader("Content-Type"))


    _, err = io.Copy(c.Writer, download)
    if err != nil {
        c.Status(http.StatusInternalServerError)
        log.Print(err)
        return
    }

    err = download.Close()
    if err != nil {
        c.Status(http.StatusInternalServerError)
        log.Print(err)
        return
    }
    
}