【问题标题】:How to use rendered template in creating a pdf如何在创建 pdf 时使用渲染模板
【发布时间】:2019-03-11 10:45:59
【问题描述】:

好的,我是使用 Echo 框架的 Go Lang,尝试构建 pdf,它将从数据库源加载数据 - 稍后会出现。

这就是我渲染 pdf html 布局的方式,

func (c *Controller) DataTest(ec echo.Context) error {
  return ec.Render(http.StatusOK, "pdf.html", map[string]interface{}{
    "name": "TEST",
    "msg":  "Hello, XXXX!",
  })
}

上述函数工作正常,并呈现 html(我为函数构建了一个临时路径)。现在我想使用该函数作为我的 html 模板来构建我的 pdf。

所以我使用wkhtmltopdf 和库"github.com/SebastiaanKlippert/go-wkhtmltopdf"

这就是我在 pdf 中呈现 html 的方式,

html, err := ioutil.ReadFile("./assets/pdf.html")

if err != nil {
    return err
}

但我需要能够更新模板,这就是我尝试渲染页面并将其放入 pdf 的原因。

但是,Echo 框架返回一个错误类型,而不是字节或字符串类型,我不确定如何更新它,以便我渲染的内容作为字节返回?

谢谢,

更新

page := wkhtmltopdf.NewPageReader(bytes.NewReader(c.DataTest(data)))

这就是我目前正在做的事情,数据只是一个 html 字符串,然后将其转换为NewReader 的字节片。

这很好用,但我想将 DataTest 函数转换为 Echo 完全呈现的 html 页面。这样做的问题是,当您返回呈现的页面时,它会作为错误类型返回。

所以我试图找出更新它的原因,所以我可以将数据作为 html 字符串返回,然后将其作为字节切片放入。

【问题讨论】:

  • 请附上您的错误信息,因为很难理解您想说什么。错误消息通常非常简单。如果它出错是因为它需要字符串但获取字节,那么您所要做的就是将其转换为字符串,如 string(yourbytes)
  • 请为您的问题添加更多示例,ioutil.ReadFile 不会在 pdf 中呈现 html...
  • 我觉得解决方法很简单。但是缺少一些重要信息。如果您在服务器上渲染它,您可以通过使用page := NewPage("https://url/to/your/page/?rid=12342") 从服务器调用渲染的html 来使用pdf API,或者为什么这不可能?你如何处理模板?显示你在哪里解析和执行它。为什么要读文件?那是模板还是渲染的html?
  • @apxp 那是我能做的。我试图在不将其添加到路由器的情况下使用该功能。
  • 没有错误,因为它的类型错误。我已经用更多信息更新了我的问题

标签: pdf go


【解决方案1】:

如果你想渲染 html,使用 echo' 自定义中间件。希望对你有帮助。

ma​​in.go

package main

import (
    "bufio"
    "bytes"
    "errors"
    "fmt"
    "html/template"
    "io"
    "net"

    "net/http"

    "github.com/labstack/echo"
)

type TemplateRegistry struct {
    templates map[string]*template.Template
}

func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
    tmpl, ok := t.templates[name]
    if !ok {
        err := errors.New("Template not found -> " + name)
        return err
    }
    return tmpl.ExecuteTemplate(w, "base.html", data)
}

func main() {
    e := echo.New()

    templates := make(map[string]*template.Template)
    templates["about.html"] = template.Must(template.ParseFiles("view/about.html", "view/base.html"))
    e.Renderer = &TemplateRegistry{
        templates: templates,
    }

    // add custom middleware
    // e.Use(PdfMiddleware)

    // only AboutHandler for Pdf
    e.GET("/about", PdfMiddleware(AboutHandler))

    // Start the Echo server
    e.Logger.Fatal(e.Start(":8080"))
}

// custom middleware
func PdfMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) (err error) {
        resBody := new(bytes.Buffer)
        mw := io.MultiWriter(c.Response().Writer, resBody)
        writer := &bodyDumpResponseWriter{Writer: mw, ResponseWriter: c.Response().Writer}
        c.Response().Writer = writer

        if err = next(c); err != nil {
            c.Error(err)
        }

        // or use resBody.Bytes()
        fmt.Println(resBody.String())
        return
    }
}

type bodyDumpResponseWriter struct {
    io.Writer
    http.ResponseWriter
}

func (w *bodyDumpResponseWriter) WriteHeader(code int) {
    w.ResponseWriter.WriteHeader(code)
}

func (w *bodyDumpResponseWriter) Write(b []byte) (int, error) {
    return w.Writer.Write(b)
}

func (w *bodyDumpResponseWriter) Flush() {
    w.ResponseWriter.(http.Flusher).Flush()
}

func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
    return w.ResponseWriter.(http.Hijacker).Hijack()
}

func AboutHandler(c echo.Context) error {
    return c.Render(http.StatusOK, "about.html", map[string]interface{}{
        "name": "About",
        "msg":  "All about Boatswain!",
    })
}

查看/about.html

{{define "title"}}
  Boatswain Blog | {{index . "name"}}
{{end}}

{{define "body"}}
  <h1>{{index . "msg"}}</h1>
  <h2>This is the about page.</h2>
{{end}}

view/base.html

{{define "base.html"}}
<!DOCTYPE html>
  <html>
    <head>
      <title>{{template "title" .}}</title>
    </head>
    <body>
      {{template "body" .}}
    </body>
  </html>
{{end}}

【讨论】:

  • 这看起来不错。我尽可能地给它一个好东西,我现在只是使用一个字符串。但是您将其设置为使用 URL,这将使具有该路由的任何人都可以访问该功能。那是我不想要的。我正在寻找一个不通过路由器并且可以在另一个函数中调用的函数。因此我不确定中间件是否可以工作?我认为这仅适用于路由功能吗?
  • 如果你想只对一个API应用中间件,你可以举个例子。我更新了示例。
【解决方案2】:

所以据我了解你想要:

  1. 从模板渲染 HTML
  2. 将 HTML 转换为 PDF
  3. 将其作为 HTTP 响应发送?您的问题不清楚这部分,但这并不重要。

所以,Echo 之所以返回error 是因为它实际上不仅进行了模板渲染,而且还向客户端发送了响应。如果你想在两者之间做些别的事情,你不能使用 echo 中的那个方法。

幸运的是,echo 在那里没有做任何神奇的事情。您可以使用标准模板包获得相同的结果。

func GetHtml(filename string, data interface{}) (string, error) {
    filedata, err := ioutil.ReadFile(filename)
    if err != nil {
        return "", err
    }

    asString := string(filedata)
    t, err := template.New("any-name").Parse(asString)
    if err != nil {
        return "", err
    }

    var buffer bytes.Buffer
    err = t.Execute(&buffer, data)
    if err != nil {
        return "", err
    }

    return buffer.String(), nil
}

你的函数返回string。如果愿意,您可以使用buffer.Bytes() 来创建一个字节数组。

在此之后,您可以做任何您需要的事情,例如转换为 PDF 并使用 echoCtx.Response().Writer() 将其写回给客户。

希望对您有所帮助,但以后尝试提出更精确的问题,您更有可能收到准确的答复。

【讨论】:

  • 我不确定您的数据变量在做什么?在我的 DataTest(第 1 版)中,我返回了一个渲染的 html 页面(由 Echo 提供),其中还包括一个发送到模板中以添加到内容中的数据映射。到目前为止我所做的只是渲染一个字符串 html(更新 DataTest)。在 PDF 插件 github 页面上,有一种方法可以呈现打开的文件,这不是我想要的。我需要渲染它,例如内容添加到模板中,该模板作为字符串/字节片传回给我
  • @C0ol_Cod3r data 是您模板的参数。请看golang.org/pkg/html/template
猜你喜欢
  • 2014-07-23
  • 2011-06-05
  • 1970-01-01
  • 2011-10-20
  • 2016-11-13
  • 2021-06-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多