Golang Chromedp: pdf file download without saving in server

616 Views Asked by At

How to chromedp pdf download without saving in server? Below code is working for generating pdf file and saving in server side. But I want to download pdf file without saving in server side.

func PDFInvoice(c *gin.Context) {
session := sessions.Default(c)
id := c.Params.ByName("id")
token := session.Get("login_session").(string)

// create context
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()

// capture pdf
var buf []byte
url := "http://localhost:8080/invoice/" + id + "/" + token
if err := chromedp.Run(ctx, printToPDF(url, &buf)); err != nil {

    log.Fatal(err)
}
buff := new(bytes.Buffer)
if _, err := buff.WriteTo(c.Writer); err != nil {
    panic(err)
}

 if err := os.WriteFile("sample.pdf", buf, 0o644); err != nil {
    log.Fatal(err)
 }

//ioutil.WriteFile("sample.pdf", buf, 0644)
c.JSON(200, id+" "+token)
}

// print a specific pdf page.
    func printToPDF(urlstr string, res *[]byte) chromedp.Tasks {
      return chromedp.Tasks{
       chromedp.Navigate(urlstr),
       chromedp.ActionFunc(func(ctx context.Context) error {
        buf, _, err := page.PrintToPDF().WithPrintBackground(false).Do(ctx)
        if err != nil {
            return err
        }
        *res = buf
        return nil
      }),
    }
   }
1

There are 1 best solutions below

4
Zeke Lu On

You can write the bytes to http.ResponseWriter directly. See the demo below:

package main

import (
    "context"
    "log"
    "net/http"
    "sync"

    "github.com/chromedp/cdproto/page"
    "github.com/chromedp/chromedp"
)

func main() {
    http.Handle("/pdf", http.HandlerFunc(servePDF))

    log.Fatal(http.ListenAndServe(":8080", http.DefaultServeMux))
}

func servePDF(w http.ResponseWriter, r *http.Request) {
    buf, err := createPDF()

    if err != nil {
        log.Fatalln(err)
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/pdf")
    w.Write(buf)
}

func createPDF() ([]byte, error) {
    ctx, cancel := newTabContext()
    defer cancel()

    html := `<html>
<body>
<div>text</div>
<img src="https://pkg.go.dev/static/shared/gopher/package-search-700x300.jpeg"/>
<img src="https://go.dev/images/gophers/motorcycle.svg"/>
<img src="https://go.dev/images/go_google_case_study_carousel.png" />
</body>
</html>`

    var buf []byte
    if err := chromedp.Run(ctx,
        chromedp.Navigate("about:blank"),
        // set the page content and wait until the page is loaded (including its resources).
        chromedp.ActionFunc(func(ctx context.Context) error {
            lctx, cancel := context.WithCancel(ctx)
            defer cancel()
            var wg sync.WaitGroup
            wg.Add(1)
            chromedp.ListenTarget(lctx, func(ev interface{}) {
                if _, ok := ev.(*page.EventLoadEventFired); ok {
                    // It's a good habit to remove the event listener if we don't need it anymore.
                    cancel()
                    wg.Done()
                }
            })

            frameTree, err := page.GetFrameTree().Do(ctx)
            if err != nil {
                return err
            }

            if err := page.SetDocumentContent(frameTree.Frame.ID, html).Do(ctx); err != nil {
                return err
            }
            wg.Wait()
            return nil
        }),
        chromedp.ActionFunc(func(ctx context.Context) error {
            var err error
            buf, _, err = page.PrintToPDF().WithPrintBackground(false).Do(ctx)
            if err != nil {
                return err
            }
            return nil
        }),
    ); err != nil {
        return nil, err
    }

    return buf, nil
}

var (
    browserCtx context.Context
    once       sync.Once
)

// newTabContext creates a tab context with the global browser context as its parent context.
//
// When tasks is run with the returned context, a new tab will be created in the browser.
func newTabContext() (context.Context, context.CancelFunc) {
    once.Do(func() { initBrowser() })

    if browserCtx == nil || browserCtx.Err() != nil {
        log.Fatalf("browser is not available: %v", browserCtx.Err())
    }

    return chromedp.NewContext(browserCtx)
}

// initBrowser starts a browser in which to create new tab for running tasks.
func initBrowser() {
    browserCtx, _ = chromedp.NewContext(context.Background())

    // to start the browser
    if err := chromedp.Run(browserCtx); err != nil {
        log.Fatal(err)
    }
}

Usage:

  1. go run main.go
  2. curl http://localhost:8080/pdf > sample.pdf

References:

  1. https://github.com/chromedp/chromedp/issues/941
  2. https://github.com/chromedp/chromedp/issues/836