How to make pdf download in shiny app response to user inputs?
I apologize that it took me this long to get back to this. After looking at what I've done, it turns out it was a little more involved than I remembered.
Here's my example app code
library(shiny)
library(ggplot2)
library(magrittr)
ui <- shinyUI(
fluidPage(
column(
width = 2,
selectInput(
inputId = "x_var",
label = "Select the X-variable",
choices = names(mtcars)
),
selectInput(
inputId = "y_var",
label = "Select the Y-variable",
choices = names(mtcars)
),
selectInput(
inputId = "plot_type",
label = "Select the plot type",
choices = c("scatter plot", "boxplot")
),
downloadButton(
outputId = "downloader",
label = "Download PDF"
)
),
column(
width = 3,
tableOutput("table")
),
column(
width = 7,
plotOutput("plot")
)
)
)
server <- shinyServer(function(input, output, session){
#****************************************
#* Reactive Values
table <- reactive({
mtcars[, c(input[["x_var"]], input[["y_var"]])]
})
plot <- reactive({
p <- ggplot(data = mtcars,
mapping = aes_string(x = input[["x_var"]],
y = input[["y_var"]]))
if (input[["plot_type"]] == "scatter plot")
{
p + geom_point()
}
else
{
p + geom_boxplot()
}
})
#****************************************
#* Output Components
output$table <-
renderTable({
table()
})
output$plot <-
renderPlot({
plot()
})
#****************************************
#* Download Handlers
output$downloader <-
downloadHandler(
"results_from_shiny.pdf",
content =
function(file)
{
rmarkdown::render(
input = "report_file.Rmd",
output_file = "built_report.pdf",
params = list(table = table(),
plot = plot())
)
readBin(con = "built_report.pdf",
what = "raw",
n = file.info("built_report.pdf")[, "size"]) %>%
writeBin(con = file)
}
)
})
shinyApp(ui, server)
And here is my RMD (entitled report_file.Rmd
)
---
title: "Parameterized Report for Shiny"
output: pdf_document
params:
table: 'NULL'
plot: 'NULL'
---
```{r}
params[["plot"]]
```
```{r}
params[["table"]]
```
Some highlights to look for
- Notice the exists of
params
in the YAML front matter of the RMarkdown script. This allows us to pass in a list of values to be used in the script when we invokermarkdown::render(..., params = list(...))
- I always build my PDF to a dummy file. That way it's easy to find.
- The reason I always build to a dummy file is that to get the download handler to work, you need to read the bit-content of the PDF and push it to the
file
argument usingwriteBin
. See mydownloadHandler
construction. - Using the parameterized report means you don't have to recreate your outputs in the rmarkdown script. The work was done in the Shiny app, the parameterized report just helps you send the objects correctly.
It isn't quite the same as passing files back and forth (although if it could be that easy, I'd love to know it).
Read more about parameterized reports here: http://rmarkdown.rstudio.com/developer_parameterized_reports.html
Shiny app run code to generate a pdf then offer that pdf to user for download
You are using the downloadHandler
incorrectly. You do not need an observe()
-function here, as shiny provides "reactivity", meaning you can bind objects to ui-elements which are updated whenever the object changes.
Therefore, you only need to assign a downloadHandler
to your download-button and tell it how to generate the file you want to provide:
library(shiny)
ui <- fluidPage( # just the download-button and a textInput for the filename
textInput("pdfname", "Filename", "My.pdf"),
downloadButton("outputButton", "Download PDF")
)
server <- function(session, input, output) {
# No need for an observer! Just assign the downloadHandler to the button.
output$outputButton <- downloadHandler(input$pdfname, function(theFile) {
# The first parameter is the name given to the file provided for download to the user.
# The parameter in the function (theFile) is a placeholder for the name that is later
# assigned to the download-file.
# Now you can call your pdf-generating function...
makePdf()
# ... and use file.copy to provide the file "in" the save-button
file.copy(from = "/results/myGenerated.pdf", to = theFile)
})
}
# Sample pdf-generating function:
makePdf <- function(){
pdf(file = "/results/myGenerated.pdf")
plot(cars)
dev.off()
}
shinyApp(ui = ui, server = server)
However, you do not need to store your files first, you might want to directly save them "in" the download button:
library(shiny)
ui <- fluidPage( # As above
textInput("pdfname", "Filename", "My.pdf"),
downloadButton("outputButton", "Download PDF")
)
server <- function(input, output) {
output$outputButton <- downloadHandler(input$pdfname, function(theFile) {
# Here, your pdf-generator is provided with the "filename" that is used
# to provide the file for the user.
makePdf(theFile)
})
}
# Sample pdf-generating function:
makePdf <- function(filename){
pdf(file = filename)
plot(cars)
dev.off()
}
shinyApp(ui = ui, server = server)
Producing PDF report using user input files - Shiny
Problem because in shiny
data_set
is reactive
so you cant use it like
<<echo = FALSE , message = F, >>=
data_set
@
in your .Rnw
So try
.Rnw
\documentclass{article}
\usepackage{tabularx}
\begin{document}
\begin{table}[H]
\begin{tabularx}
\textbf{Test Name:}& \Sexpr{input$filename} \\
\textbf{Date:}& \\
\end{tabularx}
\end{table}
<<echo = FALSE , message = F, >>=
data_set_1
@
\end{document}
handler
downloadHandler(
filename = reactive({paste0(input$filename,'.pdf')}),
content = function(file) {
data_set_1=data_set()
out = knit2pdf('pdf_shell.Rnw', clean = TRUE)
file.rename(out, file) # move pdf to file for downloading
},
contentType = 'application/pdf'
)
Or may be simple data_set()
in .Rnw
Download handler in R shiny does not produce a PDF file (using rmarkdown::render())
Here is a working solution if you have latex installed:
#LIBRARIES
library (shiny)
library(shinydashboard)
library (ggplot2)
library (dplyr)
blues <- c( "#013560", "#0065AD", "#007BD3", "#0091F9", "#9FD4F9", "#EEEEEE")
#sidebar vordefinieren
Codepan <- div(
id = 'sidebar_cr',
actionButton (inputId = "Button", label = "Go!"),
"some intro text and then the download button",
downloadButton("report", "Meine Ergebnisse als PDF speichern")
)
sidebar <-
dashboardSidebar(Codepan)
#Body vordefinieren
body <- dashboardBody(
fluidRow(
box(title = "Deine Mediennutzung",
status= "success", solidHeader = TRUE, height=400,width = 11,
plotOutput(outputId= "PlotM", height= 300))
),
fluidRow(
box(
width = 11, title = "Deine Ergebnisse", solidHeader = TRUE, status = "success",
column(width= 6, plotOutput("plotEAT", height = 250))
))
)
# hier beginnt die eigentliche App:
ui <-
dashboardPage(
dashboardHeader(title = "title", disable = FALSE),
sidebar,
body
)
server <- function(input, output) {
#2 Plot comparison Feedback
MediaCompare3 <- eventReactive(input$Button, {
det <-data.frame(group = c("a-du", "a-ges", "b-sport", "c-age"),
means = c(16, 22, 31, 15)
)
})
plotM <- reactive({
ggplot(MediaCompare3(), aes(x = group, y = means)) +
geom_bar(stat = "identity", fill = "#013560", color = "#013560") +
# scale_x_discrete(labels=c("Du", "Gesamtdurchschnitt",paste("Durchschnitt",Daten[toupper(input$Code), "Sportart_zurueckkodiert"]), paste("Durchschnitt", (Daten[toupper(input$Code), "SP01_01"]-1),"-",(Daten[toupper(input$Code), "SP01_01"]+1), "Jahre" )))+
xlab(NULL) +
ylab("Mediennutzung in Minuten")+
#geom_hline(yintercept = as.numeric(as.character(Daten[toupper(input$Code), "Nutzungsdauer_DM_Gesamt"])), lty = 8, col = "Red")+
geom_text(aes(label = round(means, 0)), vjust =2, colour = "white", size= 8)#+
#geom_label(label="Du", x = 0.5,y = as.numeric(as.character(Daten[toupper(input$Code), "Nutzungsdauer_DM_Gesamt"])), color ="red")
})
output$PlotM <- renderPlot({
plotM()
})
forplotEAT <- eventReactive (input$Button, {
df<- data.frame(Komp = rep(c("Einstellungen zu Essen", "Sozialer Vergleich"), 3),
groupW = c("ADu", "ADu",
"AGesamt", "AGesamt",
"ZGeschlecht","ZGeschlecht"),
valuesW = c (19, 20, 21, 34, 12, 17
))
})
plotEAT <- reactive({
Geschlecht <- "girls"
ggplot(forplotEAT(), aes(x = Komp, y = valuesW, fill = groupW)) +
geom_bar(position = "dodge", stat = "identity",color = "#404040", show.legend = TRUE)+
#scale_fill_discrete(name = "Vergleichsgruppen", labels=c("Du", "Gesamtdurchschnitt", Geschlecht))+
xlab("Gruppe")+
ylab("Ergebnis")+
geom_text(aes(label = round(valuesW, 1)), vjust =2, colour = "white", size= 5, position = position_dodge(width= 0.9))+
scale_fill_manual(values= blues, name = "Vergleichsgruppen", labels=c("Du", "Gesamtdurchschnitt", Geschlecht))#+
#coord_flip()+
#geom_hline(yintercept = 10, lty = 8, col = "Red")
})
output$plotEAT <-renderPlot ({
plotEAT()
})
output$report<-
downloadHandler(
"Result.pdf",
content =
function(file){
rmarkdown::render(
input= "report_file.Rmd",
output_file = "built_report.pdf",
params = list (plotM = plotM(),
plotEAT= plotEAT())
)
readBin (con = "built_report.pdf",
what = "raw",
n = file.info ("built_report.pdf")[, "size"])%>%
writeBin (con = file)
} )
}
shinyApp(ui=ui, server=server)
How to download a PDF file in a Shiny app
Take a look in the downloadHandler
function documentation, it has two arguments without default values: filename and content.
filename is basically the name of the file that will be downloaded. It has not to be inside a function. filename = "your-pdf-name.pdf"
works as much as defining it inside the argumentless function.
content, in the other hand, creates a tempfile with the content that is going to be downloaded. In most cases you're going to create a file that is going to be fulfilled with something you have created in you app.
How that is not your case, my solution provides something we call "gambiarra" in Brasil: it copies the file you want to download to the tempfile that shiny needs to the downloadHandler
works. (I've tried just define it as the path to the file but it doesn't work)
ui <- fluidPage(
downloadLink("downloadData", "Download")
)
server <- function(input, output) {
output$downloadData <- downloadHandler(
filename = "your-pdf-name.pdf",
content = function(file) {
file.copy("www/teste.pdf", file)
}
)
}
shinyApp(ui, server)
Download DT::datatable as pdf in shiny app
You can try the pdf
button of the Buttons
datatables extension. In this way you don't need a downloadHandler
. Otherwise, below is a solution using the good old xtable
package. For more sophisticated tables, use kableExtra
.
library(shiny)
library(DT)
library(xtable)
library(withr)
library(shinybusy)
ui <- fluidPage(
add_busy_spinner(spin = "cube-grid", onstart = FALSE),
column(
width = 3,
downloadButton(
outputId = "downloader",
label = "Download PDF"
)
),
column(
width = 3,
DTOutput("table")
)
)
server <- function(input, output, session){
table <- reactive({
mtcars
})
#****************************************
#* Output Components
output[["table"]] <- renderDT({
datatable(table())
})
#****************************************
#* Download Handlers
output[["downloader"]] <- downloadHandler(
filename = "results_from_shiny.pdf",
content = function(file){
texfile <- paste0(tools::file_path_sans_ext(file), ".tex")
latex <- print.xtable(
xtable(table()), print.results = FALSE,
floating = FALSE, scalebox = "0.7"
)
writeLines(
c(
"\\documentclass[12pt]{standalone}",
"\\usepackage{graphics}",
"\\usepackage{caption}",
"\\begin{document}",
"\\minipage{\\textwidth}",
latex,
"\\captionof{table}{My caption}",
"\\endminipage",
"\\end{document}"
),
texfile
)
with_dir(
dirname(texfile),
tools::texi2pdf(texfile, clean = TRUE)
)
}
)
}
shinyApp(ui, server)
Export R Shiny Page to PDF
First of all , you should really produce a reproducible example not just a sample of your code. We should copy and paste your code and it will run.
The idea
Since you are using
ggplot2
which is king ofgrid
plots, I think one easy option to save plots/tables is to usegridExtra
package. Usinggrid.arrange
orarrangeGrobs
you can save your grobs to predefined device. Then, downloadhandler will do the download.To not regenerate all the plots each time, I think one solution is to save them in a global variable that you update each time you change the plot. Here
reactiveValues
come in rescue to store plots and tables ad dynamic variable.
Solution
ui.R
library(shiny)
shinyUI(fluidPage(
# Application title
titlePanel("Save ggplot plot/table without regenration"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
downloadButton('export')
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("p1"),
plotOutput("p2"),
tableOutput("t1")
)
)
))
server.R
library(shiny)
library(ggplot2)
library(gridExtra)
shinyServer(function(input, output) {
## vals will contain all plot and table grobs
vals <- reactiveValues(p1=NULL,p2=NULL,t1=NULL)
## Note that we store the plot grob before returning it
output$p1 <- renderPlot({
vals$p1 <- qplot(speed, dist, data = cars)
vals$p1
})
output$p2 <- renderPlot({
vals$p2 <- qplot(mpg, wt, data = mtcars, colour = cyl)
vals$p2
})
## same thing for th etable grob
output$t1 <- renderTable({
dx <- head(mtcars)
vals$t1 <- tableGrob(dx)
dx
})
## clicking on the export button will generate a pdf file
## containing all grobs
output$export = downloadHandler(
filename = function() {"plots.pdf"},
content = function(file) {
pdf(file, onefile = TRUE)
grid.arrange(vals$p1,vals$p2,vals$t1)
dev.off()
}
)
})
Upload an image to R shiny downloadable to pdf
The issue is that RenderImage will delete the file after usage unless deleteFile = False. This is the line to change, Remove this:
re1 <- reactive({gsub("\\\\", "/", input$file1$datapath)})
Add this:
output$uploaded_image <- renderImage({list(src = input$file1$datapath)}, deleteFile = FALSE)
Related Topics
Force Error Bars to Be in the Middle of Bar
How to Play Birthday Music Using R
How to Convert a Huge List-Of-Vector to a Matrix More Efficiently
Replace Characters from a Column of a Data Frame R
How to Skip an Error in a Loop
How to Preserve Base Data Frame Rownames Upon Filtering in Dplyr Chain
Create Tables with Conditional Formatting with Rmarkdown + Knitr
Adding Elements to a List in for Loop in R
In R, Extract Part of Object from List
R Markdown: How to Make Text Float Around Figures
Data.Table in R - Multiple Filters Using Multiple Keys - Binary Search
How to Conditionally Replace Values in R Data Frame Using If/Then Statement
Relative Positioning of Geom_Text in Ggplot2
R: How to Recode Multiple Variables at Once
Creating R Package, Warning: Package '---' Was Built Under R Version 3.1.2