英文:
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)
	}
}
用法:
go run main.gocurl http://localhost:8080/pdf > sample.pdf
参考资料:
英文:
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:
go run main.gocurl http://localhost:8080/pdf > sample.pdf
References:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论