Golang Chromedp:在不保存到服务器的情况下下载 PDF 文件

huangapple go评论89阅读模式
英文:

Golang Chromedp: pdf file download without saving in server

问题

如何在不保存在服务器端的情况下使用chromedp下载PDF文件?

下面的代码可以生成PDF文件并保存在服务器端。但是我想要在不保存在服务器端的情况下下载PDF文件。

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

	// 创建上下文
	ctx, cancel := chromedp.NewContext(context.Background())
	defer cancel()

	// 捕获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)
	}

	c.JSON(200, id+" "+token)
}

// 打印指定的PDF页面。
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
		}),
	}
}

以上是给出的代码,它可以在服务器端生成PDF文件并保存。如果你想要在不保存在服务器端的情况下下载PDF文件,你需要对代码进行修改。

英文:

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

得分: 2

你可以直接将字节写入http.ResponseWriter。请参考下面的示例代码:

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"),
		// 设置页面内容并等待页面加载完成(包括其资源)。
		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 {
					// 如果不再需要事件监听器,将其移除是一个好习惯。
					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 使用全局浏览器上下文作为其父上下文创建一个标签上下文。
//
// 当使用返回的上下文运行任务时,将在浏览器中创建一个新的标签页。
func newTabContext() (context.Context, context.CancelFunc) {
	once.Do(func() { initBrowser() })

	if browserCtx == nil || browserCtx.Err() != nil {
		log.Fatalf("浏览器不可用:%v", browserCtx.Err())
	}

	return chromedp.NewContext(browserCtx)
}

// initBrowser 启动一个浏览器,用于创建新的标签页以运行任务。
func initBrowser() {
	browserCtx, _ = chromedp.NewContext(context.Background())

	// 启动浏览器
	if err := chromedp.Run(browserCtx); err != nil {
		log.Fatal(err)
	}
}

用法:

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

参考资料:

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

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

package main

import (
	&quot;context&quot;
	&quot;log&quot;
	&quot;net/http&quot;
	&quot;sync&quot;

	&quot;github.com/chromedp/cdproto/page&quot;
	&quot;github.com/chromedp/chromedp&quot;
)

func main() {
	http.Handle(&quot;/pdf&quot;, http.HandlerFunc(servePDF))

	log.Fatal(http.ListenAndServe(&quot;:8080&quot;, 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(&quot;Content-Type&quot;, &quot;application/pdf&quot;)
	w.Write(buf)
}

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

	html := `&lt;html&gt;
&lt;body&gt;
&lt;div&gt;text&lt;/div&gt;
&lt;img src=&quot;https://pkg.go.dev/static/shared/gopher/package-search-700x300.jpeg&quot;/&gt;
&lt;img src=&quot;https://go.dev/images/gophers/motorcycle.svg&quot;/&gt;
&lt;img src=&quot;https://go.dev/images/go_google_case_study_carousel.png&quot; /&gt;
&lt;/body&gt;
&lt;/html&gt;`

	var buf []byte
	if err := chromedp.Run(ctx,
		chromedp.Navigate(&quot;about:blank&quot;),
		// 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&#39;s a good habit to remove the event listener if we don&#39;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(&quot;browser is not available: %v&quot;, 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 &gt; sample.pdf

References:

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

huangapple
  • 本文由 发表于 2023年2月4日 01:35:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75339208.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定