Golang中HTML转PDF报错问题解决方案

Golang中HTML转PDF报错问题解决方案 我想将HTML文件导出为PDF文件。我尝试使用来自 https://pkg.go.dev/github.com/SebastiaanKlippert/go-wkhtmltopdf 的 go-wkhtmltopdf 包。 当我尝试使用时,遇到了错误: fork/exec : no such file or directory

	var body bytes.Buffer
	str := "<!DOCTYPE html>\n<html>\n    test\n</html>"
	body.WriteString(str)

	pdfGenerator, err := pdf.NewPDFGenerator()

	page := pdf.NewPageReader(bytes.NewReader(body.Bytes()))
	pdfGenerator.AddPage(page)

	err = EnsureBaseDir("./pdfFiles/test.pdf")
	if err != nil {
		fmt.Println("EnsureBaseDir failed error: ", err)
	}

	file, err := os.Create("./pdfFiles/test.pdf")
	if err != nil {
		fmt.Println("os.Create error: ", err)
	}
	defer file.Close()

	pdfGenerator.SetOutput(file)
	err = pdfGenerator.Create()
	if err != nil {
		fmt.Println("pdfGenerator.Create error: ", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

pdfGenerator.Create 的调用产生了这个错误。我最终的目的是从一个 Go 语言的 HTML 模板创建 HTML,并且我最初使用了 templates.ExecuteTemplate 的输出,但为了简化,我创建了这个简单的 HTML 字符串,但仍然遇到了同样的错误。

也许有更好的包可以完成这个任务,但这个包在很多网站上都被推荐为合适的选择。


更多关于Golang中HTML转PDF报错问题解决方案的实战教程也可以访问 https://www.itying.com/category-94-b0.html

21 回复

我不明白,我的服务器没有进行任何重定向。

更多关于Golang中HTML转PDF报错问题解决方案的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我之前不知道这个。我会看看能否安装它。

是的——乍一看,他似乎忘了安装 wkhtmltopdf。我之前用过那个 wkhtmltopdf 包装器,在新环境设置中,依赖那个可执行文件总是很麻烦。

也许是因为你没有遵循301重定向,导致传递的HTML无效?浏览器会自动处理重定向。

rschluet:

Protocol "about" is unknown

或者可能是重定向的URL有问题。

你是否按照链接中的描述,将HTML修改为CSS文件的绝对路径?

已确认通过添加样式表的绝对路径解决了此问题。

你已经超出了Go封装器的范畴,并遇到了底层程序的问题。

这可能与以下问题有关: https://stackoverflow.com/questions/62315246/wkhtmltopdf-0-12-6-warning-blocked-access-to-file 其中提到需要启用本地文件访问,但我还没有找到设置的方法。

// 此处为Go代码示例,实际内容需根据HTML中的代码部分进行转换
// 由于提供的HTML片段不包含Go代码,此处仅为格式示例
func example() {
    // 代码保持原样,不翻译
}

抱歉,我猜我把它弄得太简单了,而且没有检查,因为它渲染得很好。不,不是那个问题。PDF 仍然没有样式。

我添加了这行代码:

page.EnableLocalFileAccess.Set(true)

这修复了 301 错误。

你用过这个包吗?

嘿,Dean,我尝试了你所有的建议,但都没成功。除了把样式放在 <style> 标签里。我实际的样式表非常长,并且被我的几乎所有模板(大约20个)所使用。如果那样做,维护起来将是一场噩梦。不过我还是试了,它确实有效。接下来,我打算尝试深入包装器代码。也许我能弄清楚路径是在哪里出错的。

您是否知道这要求您拥有 wkhtmltopdf 程序,并且需要通过 wkhtmltopdf package - github.com/SebastiaanKlippert/go-wkhtmltopdf - pkg.go.dev 来让包指向它?

我将href更改为: href="/styles/tutorial.css" 这在浏览器中有效,但PDF仍然没有样式

如果我更改为 href="../styles/tutorial.css",go包装器不会为PDF应用样式 如果我从templates文件夹运行命令行 wkhtmltopdf --enable-local-file-access tutorial.html test.pdf 生成的PDF文件是有样式的。 我的目录结构如下:

serverRoot
    templates
         tutorial.html
    styles
         tutorialStyle.css

我猜测是JavaScript文件的路径问题。请务必设置标准错误输出:

pdfGenerator.SetStderr(os.Stdout)

这会将警告信息打印到控制台,我猜其中应该有一些警告(例如那个图像文件)。

看起来它也不支持 display: grid

是的:

CSS grid layout - supported?

你好,

我想知道 wkhtmltopdf 是否支持 CSS 网格布局?

我似乎无法让它工作。

谢谢

rschluet:

Error: Failed to load about:blank, with network status code 301 and http status code 0 - Protocol "about" is unknown

抱歉,我误将301理解为了HTTP状态码。如果我当时看得更仔细些,就会发现并非如此。不过,看起来它似乎是在尝试将“about:blank”作为一个URL来加载。关于那2300页的问题,这可能仍然是一个误导性的线索。我会尝试精简“当前资产”之前的HTML代码,以找出导致所有分页符的原因。

如果将样式链接的 href 设置为浏览器获取该样式文件的确切 URL,会发生什么?例如:

<!-- 确保你可以将 href 的内容粘贴到浏览器中,并且它能正常工作 -->
<link rel="stylesheet" type="text/css" href="http://localhost:123/styles/tutorial.css">

将端口号更改为你正在使用的端口。并且,在你去生成 PDF 时,确保提供此内容的 Go 应用程序正在运行。

另外,关于内联样式:你可以保持样式表独立,但在生成 PDF 时,只需在模板中将其内容以内联方式注入。

终于有东西能用了。

    <base href="http://localhost">
    <link rel="stylesheet" type="text/css" href="/styles/tutorial.css">

我很高兴它能工作,因为单步调试 exec cmd 代码都快让我的大脑沸腾了。我看到了你最近的帖子,也看了链接的那个问题。希望他们正在处理。我有一个缩略图没有在 PDF 中显示出来,但它不是从文件加载的,而是我保存在数据库中的 base64 字符串。它能找到样式表并且正在使用它。我在想你的‘base’建议是否对那个未解决的问题有效。缩略图不是那么重要。然而,它也没有执行 JavaScript,我主要用 JavaScript 来格式化数字,而 PDF 有 370 页长,实际的 HTML 输出在最后 2 页。我找到了一个禁用 JavaScript 的选项,但没有启用它的选项,所以我猜默认是启用的。

浏览器输出: Image 3-23-22 at 1.31 PM

PDF 输出: Image 3-23-22 at 1.30 PM

看起来它也不支持 display: grid。

我刚刚重新运行了它,但移除了从流动资产开始的所有内容,同样的错误:

[===========================================================>] Page 2302 of 2304
[===========================================================>] Page 2303 of 2304
[============================================================] Page 2304 of 2304
Done
Exit with code 1 due to network error: ProtocolUnknownError

我移除了样式表的链接,没有错误,但渲染效果仍然相差甚远。PDF 长达 2000 页,金额数字完全没有显示出来。 PDF 的最后一页: Image 3-22-22 at 1.00 PM

在浏览器中渲染的效果: Image 3-22-22 at 1.01 PM

顺便说一句,使用样式表看起来会好很多。

我对这个包感到非常沮丧。我无法让它在一个最简单的例子上工作。

这是 HTML 代码:

<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" type="text/css" href="../styles/tutorial.css">
</head>
<html>
    <h2>test</h2>
    <div class="container">
        container
    </div>
</html>

以及样式表:

.container {
    border: 1px solid blue;
}

在浏览器中看起来很好,但生成的 PDF 看起来像这样: Image 3-22-22 at 3.07 PM 注意没有蓝色边框 而且我在 pdfGenerator.Create() 上没有收到任何错误。

试试这个

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" type="text/css" href="../styles/tutorial.css">
</head>
<body>
    <h2>test</h2>
    <div class="container">
        container
    </div>
</body>
</html>

可能不会有区别,因为HTML渲染器通常非常宽松。

图片

: The HTML Document / Root element - HTML: HyperText Markup Language | MDN

图片

<html> HTML元素表示HTML文档的根(顶级元素),因此也被称为根元素。所有其他元素都必须是此元素的后代。

图片

: The Document Metadata (Header) element - HTML: HyperText Markup Language | MDN

图片

<head> HTML元素包含关于文档的机器可读信息(元数据),如其标题、脚本和样式表。

图片

: The Document Body element - HTML: HyperText Markup Language | MDN

图片

<body> HTML元素表示HTML文档的内容。一个文档中只能有一个<body>元素。

另请参阅 Css styles not applying in pdf · Issue #3676 · wkhtmltopdf/wkhtmltopdf · GitHub

Rick,你遇到了几个问题。当你使用相对路径链接到某个文件时,这取决于你执行命令的位置。在你运行 wkhtmltopdf --enable-local-file-access tutorial.html test.pdf 的例子中,看起来你是在 “templates” 文件夹中,所以相对路径 ../styles 是没问题的(在网页浏览器中运行时也是如此,因为我假设你访问的是类似 /tutorial.html 的路径)。然而,如果你要在 serverRoot 文件夹中运行同样的命令,它就会在 serverRoot 的父文件夹中寻找那个文件。

wkhtmltopdf 的情况下,问题变得更加复杂,因为它是在一个无头浏览器中执行的。查看这个 issue 了解更多细节。

如果你想快速启动并运行,一个选择是将你的样式直接放在发送给 wkhtmltopdf 的 HTML 代码块中。不要在你的头部这样写:

<head>
    <meta charset="UTF-8">
    <!-- External stylesheet -->
    <link rel="stylesheet" type="text/css" href="../styles/tutorial.css">
</head>

…而是直接将你的样式放在头部,这样你完全不需要解析文件:

<head>
    <meta charset="UTF-8">
    <!-- Inline styles -->
    <style>
    .container {
        border: 1px solid blue;
    }
    </style>
</head>

除此之外,你可以尝试添加一个 base 标签。如果你这样做会发生什么?

<head>
    <meta charset="UTF-8">
    <!-- Replace this port with your actual development server port -->
    <base href="http://localhost:8080/">
    <!-- Now this will resolve to http://localhost:8080/styles/tutorial.css
    which means that wkhtmltopdf should be able to retrieve the css from your
    go web server which is serving up the files-->
    <link rel="stylesheet" type="text/css" href="/styles/tutorial.css">
</head>

你也可以尝试将 base href 设置为 “./”,并将样式表的 href 设置为 href="/styles/tutorial.css",只要你的 Go 可执行文件在 serverRoot 目录下运行,并且你已经成功启用了本地文件访问,我认为这应该能工作。

嘿,Dean,你在这个论坛上一定很活跃。自从你上次帮助我之后,我的项目取得了巨大的进展。还有Jeff,我相信你也提供了帮助,谢谢。我今年72岁,在半退休状态学习所有这些新技术,大部分都能弄明白,但有个论坛来帮我解决那些搞不定的问题,真是太好了。

嗯,安装 wkhtmltopdf 帮我解决了那个问题。我安装了 wkhtmltopdf,并且成功地用简单的HTML创建了test.pdf,没有错误,PDF效果很好。但是,当我给它一个更复杂的HTML(在浏览器中渲染得很好,大约有30行长)时,我收到了一个巨大的错误。它确实创建了一个PDF,长达2300页,除了最后一页有一些HTML元素外,其他页面都是空的。我来展示一下结尾部分:

Current assets
Bank savings account Kiribati
Long term assets
Equipment Kiribati
Current liabilities
Interest payable Kiribati Purchase Tax payable Kiribati
Long term liabilities
Mortgage loan Kiribati
Equity
Retained earnings Kiribati
Current assets total
Long term assets total
Assets total
Current liabilities total
Long term liabilities total
Equity total
Liabilities and equity total

然后,错误通过以下代码发送到浏览器:http.Error(w, err.Error(), http.StatusInternalServerError)

再次说明,大部分是空页,但这是开头和结尾:

Loading pages (1/6)
[>                                                           ] 0%
[======>                                                     ] 10%
Warning: Blocked access to file                                   
[=====================>                                      ] 35%
Error: Failed to load about:blank, with network status code 301 and http status code 0 - Protocol "about" is unknown
[============================================================] 100%
Counting pages (2/6)                                               
[============================================================] Object 1 of 1
Resolving links (4/6)                                                       
[============================================================] Object 1 of 1
Loading headers and footers (5/6)                                           
Printing pages (6/6)
[>                                                           ] Preparing
[>                                                           ] Page 1 of 2304
[>                                                           ] Page 2 of 2304
[>                                                           ] Page 3 of 2304
[>                                                           ] Page 4 of 2304

以及结尾:

[===========================================================>] Page 2301 of 2304
[===========================================================>] Page 2302 of 2304
[===========================================================>] Page 2303 of 2304
[============================================================] Page 2304 of 2304
Done                                                                            
Exit with code 1 due to network error: ProtocolUnknownError

这个页面确实有一个缩略图,但我移除了它,结果还是一样。这个页面是一个财务报表,一个资产负债表,每个账户都有链接指向该账户的交易明细页面。也许 wkhtmltopdf 无法处理这些。页面上还有3个下拉菜单和一个按钮。

此外,我在这里发现了完全相同的问题:

github.com/SebastiaanKlippert/go-wkhtmltopdf

图像不显示

go-wkhtmltopdf 版本 1.6.1

操作系统信息 macOS Mojave 版本 10.14.6

描述 我想用 HTML 文件创建一个简单的个人简历。我尝试在右上角添加浮动图像(个人资料图片),但图像没有显示。

如何复现 这是我的示例 test.html 文件

<!DOCTYPE html>
<html>
<body style="padding: 50px; text-align: justify;">
    <h1 style="text-align: center;">Biodata</h1>
    <br>
    <div style="float: right; margin-bottom: 30px;">
      <img src="../image/profile_pic.jpeg" height="150px">
    </div>
    <div>
    <strong>Name:</strong> John
    </div>
    <br>
    <strong>Birth date:</strong> 2001-01-01
    <br>
    <br>
    <strong>Birth place:</strong> Unknown
    <br>
    <br>
    <strong>Gender:</strong> Male/Female
    <br>
    <br>
    <strong>Height:</strong> 200 cm
    <br>
    <br>
    <strong>Hobby:</strong> Sleeping
    <br>
</body>
</html>

这是 Go 代码

import (
	"bytes"
	"fmt"
	"html/template"
	"strings"

	gopdf "github.com/SebastiaanKlippert/go-wkhtmltopdf"
)

func init() {
	htmlTmp, err := template.ParseFiles("files/html/test.html")
	if err != nil {
		fmt.Println(err)
		return
	}

	buf := new(bytes.Buffer)
	err = htmlTmp.Execute(buf, nil)
	if err != nil {
		fmt.Println(err)
		return
	}

	pdfGen, err := gopdf.NewPDFGenerator()
	if err != nil {
		fmt.Println(err)
		return
	}

	pageReader := gopdf.NewPageReader(strings.NewReader(buf.String()))
	pageReader.PageOptions.EnableLocalFileAccess.Set(true)
	pdfGen.AddPage(pageReader)
	if err := pdfGen.Create(); err != nil {
		fmt.Println(err)
		return
	}

	if err := pdfGen.WriteFile("files/generated/test.pdf"); err != nil {
		fmt.Println(err)
		return
	}
}

这是我运行代码时的结果 test8.pdf

我已经使用了启用本地文件访问的选项,但图像仍然没有显示。相反,在图像应该显示的位置出现了一个方块。

预期行为 我尝试使用命令行中的 wkhtmltopdf 来查看差异

wkhtmltopdf --enable-local-file-access files/html/test.html files/generated/test.pdf

这是结果 test7.pdf

它在我希望的位置显示了图像。

这是 wkhtmltopdf 的问题,而不是封装库的问题。在你的设置代码中,设置标准错误输出:

pdfGenerator, err := pdf.NewPDFGenerator()
// 设置 stderr
pdfGenerator.SetStderr(os.Stdout)

… 你应该能在控制台上看到它试图查找你的 CSS 文件但找不到的位置。我认为你最好的选择是:

  • 将页眉转换为模板,就像我上面提到的,并在生成 PDF 时将 CSS 文件的内容作为内联样式注入。
  • 如上面问题中提到的,将 HTML 写入磁盘的临时文件中。

这个错误是因为 go-wkhtmltopdfwkhtmltopdf 命令行工具的 Go 封装,需要先安装 wkhtmltopdf 二进制文件。

解决方案:

1. 安装 wkhtmltopdf

Ubuntu/Debian:

sudo apt-get install wkhtmltopdf

macOS:

brew install wkhtmltopdf

Windows:wkhtmltopdf.org 下载并安装

2. 指定 wkhtmltopdf 路径(如果不在系统 PATH 中)

import (
    "bytes"
    "fmt"
    "os"
    pdf "github.com/SebastiaanKlippert/go-wkhtmltopdf"
)

func main() {
    // 设置 wkhtmltopdf 路径(如果需要)
    // pdf.SetPath("/usr/local/bin/wkhtmltopdf")
    
    pdfg, err := pdf.NewPDFGenerator()
    if err != nil {
        fmt.Println("NewPDFGenerator error:", err)
        return
    }
    
    htmlStr := "<!DOCTYPE html><html><body><h1>Test PDF</h1></body></html>"
    
    pdfg.AddPage(pdf.NewPageReader(bytes.NewReader([]byte(htmlStr))))
    
    // 设置 PDF 选项
    pdfg.Dpi.Set(300)
    pdfg.Orientation.Set(pdf.OrientationPortrait)
    pdfg.PageSize.Set(pdf.PageSizeA4)
    
    err = pdfg.Create()
    if err != nil {
        fmt.Println("Create error:", err)
        return
    }
    
    err = pdfg.WriteFile("./output.pdf")
    if err != nil {
        fmt.Println("WriteFile error:", err)
        return
    }
    
    fmt.Println("PDF created successfully")
}

3. 使用模板的完整示例

import (
    "bytes"
    "html/template"
    "os"
    pdf "github.com/SebastiaanKlippert/go-wkhtmltopdf"
)

func generatePDFFromTemplate(tmpl *template.Template, data interface{}, outputPath string) error {
    // 渲染模板到 buffer
    var buf bytes.Buffer
    if err := tmpl.Execute(&buf, data); err != nil {
        return err
    }
    
    // 创建 PDF 生成器
    pdfg, err := pdf.NewPDFGenerator()
    if err != nil {
        return err
    }
    
    // 添加页面
    page := pdf.NewPageReader(&buf)
    pdfg.AddPage(page)
    
    // 设置 PDF 选项
    pdfg.Dpi.Set(300)
    pdfg.Orientation.Set(pdf.OrientationPortrait)
    pdfg.PageSize.Set(pdf.PageSizeA4)
    
    // 生成 PDF
    if err := pdfg.Create(); err != nil {
        return err
    }
    
    // 写入文件
    return pdfg.WriteFile(outputPath)
}

// 使用示例
func main() {
    // 解析模板
    tmpl := template.Must(template.ParseFiles("template.html"))
    
    // 模板数据
    data := struct {
        Title   string
        Content string
    }{
        Title:   "My Document",
        Content: "This is PDF content from Go template",
    }
    
    // 生成 PDF
    if err := generatePDFFromTemplate(tmpl, data, "./output.pdf"); err != nil {
        fmt.Println("Error:", err)
        os.Exit(1)
    }
}

4. 检查 wkhtmltopdf 是否安装正确

import (
    "fmt"
    "os/exec"
)

func checkWkhtmltopdf() error {
    cmd := exec.Command("wkhtmltopdf", "--version")
    output, err := cmd.Output()
    if err != nil {
        return fmt.Errorf("wkhtmltopdf not found: %v", err)
    }
    fmt.Printf("wkhtmltopdf version: %s", output)
    return nil
}

安装 wkhtmltopdf 后,你的代码应该就能正常工作了。fork/exec : no such file or directory 错误表明系统找不到 wkhtmltopdf 可执行文件。

回到顶部