我正在按照 https://github.com/RLesur/chrome_print_shiny 中的示例构建一个 Shiny 应用程序,该应用程序将从 .Rmd 文件生成 .pdf 输出。我可以让示例 app.R 在本地运行(对于大多数选择),但修改最终将使用
render(params())
的 .Rmd 并未成功。
报告生成器.Rmd
---
title: "Test Report"
output:
pagedown::html_paged:
includes:
number_sections: false
self_contained: true
css:
- default
params:
gonna: "a"
have: "b"
some: "c"
later: "d"
#knit: pagedown::chrome_print # commented out for use in Shiny
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
## Summary
```{r cars}
summary(cars)
```
## Plot
```{r pressure}
plot(pressure)
```
app.R
library(pagedown)
library(promises)
library(rmarkdown)
library(shiny)
library(shinybusy)
chrome_extra_args <- function(default_args = c("--disable-gpu")) {
args <- default_args
# Test whether we are in a shinyapps container
if (identical(Sys.getenv("R_CONFIG_ACTIVE"), "shinyapps")) {
args <- c(args,
"--no-sandbox", # required because we are in a container
"--disable-dev-shm-usage") # in case of low available memory
}
args
}
# send the .Rmd to temp
tempReport <- tempfile(fileext=".Rmd")
file.copy("ReportGenerator.Rmd", tempReport, overwrite=TRUE)
ui <- fluidPage(
actionButton( # build the report
inputId="buildBtn",
label="Build Report"
),
uiOutput("downloadBtn") # download the report
)
server <- function(input, output) {
observeEvent(input$buildBtn, {
output$downloadBtn <- renderUI({
show_modal_spinner()
# generate PDF
chrome_print(
render(tempReport,
# will have params here later
envir = new.env(parent = globalenv())
),
output=tempfile(fileext=".pdf"),
extra_args=chrome_extra_args(),
verbose=1,
async=TRUE
)$then(
onFulfilled=function(value){
showNotification("PDF file successfully generated", type="message")
output$downloadBtn <- downloadHandler(
filename="Report.pdf",
content=function(file){
file.copy(value, file) # I think the problem is here?
},
contentType="application/pdf"
)
downloadButton(
outputId="downloadBtn",
label="Download Report"
)
},
onRejected=function(error){
showNotification(
error$message,
duration=NULL,
type="error"
)
HTML("")
}
)$finally(remove_modal_spinner)
})
})
}
shinyApp(ui = ui, server = server)
单击“构建报告”按钮确实会生成 .pdf - 使用
tempdir()
,我可以导航到会话的临时目录,并看到 .Rmd 已被连接,并且 .pdf 就在那里,命名为通常的临时文件哈希,打开时看起来与预期一致。但是,单击“下载报告”按钮不会提供 .pdf 文件。相反,我得到一个 .html 文件的对话框,该文件不包含 downloadHandler
中指示的文件名。打开它会生成一个 .html 窗口,看起来与原始 Shiny 应用程序页面类似。
鉴于 .pdf does 已生成,我猜测问题出在
downloadHandler(content)
中的某个位置,但我不太熟悉 chrome_print(async=TRUE)
如何与 library(promises)
、$then()
和临时文件一起使用将 .pdf 从 Temp 目录中拉出并将其粘贴到处理程序中。
将
output_file_path
设为 reactiveVal
。这将作为生成的 PDF 路径的存储点。此外,应利用 downloadHandler
中保留的路径独立定义 output_file_path
。
downloadHandler
然后利用此存储路径来方便下载 PDF 文件:
library(pagedown)
library(promises)
library(rmarkdown)
library(shiny)
library(shinybusy)
chrome_extra_args <- function(default_args = c("--disable-gpu")) {
args <- default_args
# Test whether we are in a shinyapps container
if (identical(Sys.getenv("R_CONFIG_ACTIVE"), "shinyapps")) {
args <- c(args,
"--no-sandbox", # required because we are in a container
"--disable-dev-shm-usage") # in case of low available memory
}
args
}
# send the .Rmd to temp
tempReport <- tempfile(fileext=".Rmd")
file.copy("ReportGenerator.Rmd", tempReport, overwrite=TRUE)
# <- changed here
ui <- fluidPage(
titlePanel("ABR Dashboard"),
actionButton(inputId = "buildBtn", label = "Build Report"),
uiOutput("downloadBtn")
)
server <- function(input, output) {
# new a path for the pdf
output_file_path <- reactiveVal()
observeEvent(input$buildBtn, {
show_modal_spinner()
# generate PDF
promise <- chrome_print(
input = render(tempReport,
# will have params here later
envir = new.env(parent = globalenv())
),
output = tempfile(fileext = ".pdf"),
extra_args = chrome_extra_args(),
verbose = 1,
async = TRUE
)
promise$then(
onFulfilled = function(value) {
output_file_path(value) # <- store to path to the generated pdf
remove_modal_spinner()
showNotification("PDF file successfully generated", type = "message")
},
onRejected = function(error) {
remove_modal_spinner()
showNotification(error$message, duration = NULL, type = "error")
}
)
})
output$downloadBtn <- renderUI({
if (!is.null(output_file_path())) {
downloadButton(outputId = "downloadPDF", label = "Download Report")
}
})
output$downloadPDF <- downloadHandler(
filename = "Report.pdf",
content = function(file) {
file.copy(output_file_path(), file, overwrite = TRUE)
},
contentType = "application/pdf"
)
}
shinyApp(ui = ui, server = server)