Search code examples
restgoswaggerswagger-2.0go-gin

How to download files using Swag golang library?


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?


Solution

  • 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
        }
        
    }