【问题标题】:How do I generate and download a PDF from markdown using Meteor如何使用 Meteor 从 Markdown 生成和下载 PDF
【发布时间】:2015-12-17 08:07:50
【问题描述】:

我有一个 Meteor 文档集合,其内容使用 markdown 设置样式(有关如何执行此操作的示例,请参阅:How does one actually use Markdown with Meteor

我的包栈如下:

npm

气氛

查看文档时,我想向我的用户展示一个Download as PDF 按钮。单击此按钮应将文档的降价转换为 PDF 文件并通过浏览器下载。

转换过程受到限制;它应该在服务器上运行,但不允许将任何临时文件保存到服务器。

我怎样才能做到这一点?

【问题讨论】:

    标签: pdf meteor markdown


    【解决方案1】:

    使用iron-router,创建一个服务器端路由来提供 PDF。

    Router.route '/api/pdf/:_id',
        name : 'generatePDF'
        where: 'server'
        action: ->
            document= Documents.findOne @params._id
            sanitize = Meteor.npmRequire 'sanitize-filename'
            filename = sanitize(document.title).replace(/\s+/g, '-')
            @response.writeHead 200,
                'Content-Type': 'application/pdf'
                'Content-Disposition': "attachment; filename=#{filename}.pdf"
    
            markdown = document.content
            Async.runSync (done)->
                Meteor.npmRequire('markdown-pdf')
                .from.string(markdown)
                .to.buffer (err, buffer)->
                    done null, new BufferStream buffer
            .result.pipe @response
    

    上面的路由接收_id 作为路由参数。这用于从Documents 集合中检索关联的document。然后通过清理document.title 将所有空格替换为连字符来生成PDF filename

    现在将响应标头设置为强制浏览器将 PDF 下载为带有经过清理的 filename 的文件。

    PDF 是使用 markdown-pdf 包从 document.content 降价生成的。这个过程因两个问题而变得复杂:

    1. 生成 PDF 的调用本质上是异步的,因此需要回调。这需要通过将其包装在 Meteor 的 Async.runSynch 方法中来转换为同步调用。这将返回一个具有我们可以使用的result 属性的对象。

    2. markdown-pdf 包有一个 to.buffer 方法,它返回一个包含生成的 PDF 的 buffer。这使我们可以将所有内容保存在代码中,并且无需将临时文件保存到服务器。要将这个buffer 通过管道传输到response,我们需要将其转换为流。我使用帮助器 BufferStream 对象为我执行此操作(见下文)

    有了这条路线,我只需在显示模板的某处放置一个“下载为 PDF”按钮(下面的代码是由 Bootstrap 3 类设置为按钮的 Jade 链接)

    a.btn.btn-primary(href='{{pathFor "generatePDF"}}' target='_blank') Download as PDF 
    

    最后,这是 BufferStream 辅助类:

    stream = Meteor.npmRequire "stream" 
    
    class @BufferStream extends stream.Readable 
    
        constructor: (@source, @offset = 0) ->
            throw new Meteor.Error 'InvalidBuffer', 'BufferStream source must be a buffer.' unless Buffer.isBuffer(@source)
            super
            @length = @source.length
    
        _read: (size) ->
            if @offset < @length
                @push @source.slice @offset, @offset + size
                @offset += size
    
            @push null if @offset >= @length
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-17
      • 2015-07-09
      • 2016-01-13
      • 1970-01-01
      • 1970-01-01
      • 2016-08-20
      • 2015-02-13
      • 1970-01-01
      相关资源
      最近更新 更多