petbadger
petbadger

Reputation: 51

Download existing docx object from R Shiny app

I am working on an R Shiny app where a user supplies information that modifies an existing Word document for the user to download. I've had trouble getting R Shiny to download the resulting new Word document. I've tried regular hyperlinks, and that doesn't seem to work.

(Edit: After typing up this post, I came across how to download files with hyperlinks. I forgot files need to be placed inside a www folder, as specified here: Shiny hyperlink relative path to a file. So although I can get my Shiny App to work using this approach, I'd still like to know why my example below is not working).

I came across Github Issue #145 (https://github.com/davidgohel/officer/issues/145) which almost has the solution. But the pptx being downloaded is created from scratch whereas I want to start from an EXISTING Word docx.

In my code example, there are 3 downloadHandler buttons:

  1. Uses the original pptx download code example from Github Issue #145
  2. The second modifies the above to download a docx
  3. The third button is my attempt to download an existing and modified docx file

The third button is not working as I had hoped. If I had to guess, I think it has to do with my template being from from read_docx. It looks like it creates some temporary file behind the scenes. But I don't know where to go from here.


For completeness, here are some related links:

Reporters package to download docx report from shiny (uses ReporeRs which is older than officer R package)

Writing word documents with the officer package: How to combine several rdocx objects? (Helpful if merging existing docx to the tempfile in my example)

downloadHandler reference: https://shiny.rstudio.com/reference/shiny/latest/downloadHandler.html



# -------- Example code ------------

library(shiny)
library(officer)
library(mschart)
library(dplyr)

# Create template folder and file. (Irrelevant if already exists.)
dir.create("www")
read_docx() %>% 
    body_add_par("My template file") %>%
    print(., target = "www/template.docx")

# Existing file as Template
mytemplate <- read_docx(path = "www/template.docx") 


# For Button 1
gen_pptx <- function(chart, file) {
    read_pptx() %>% 
        add_slide(layout = "Title and Content", master = "Office Theme") %>% 
        ph_with_chart(chart = chart) %>% 
        print(target = file)
}

chart <- data.frame(x = letters[1:3], y = 1:3) %>% 
    ms_barchart(x = "x", y = "y")


# For button 2
gen_docx <- function(file) {
    read_docx() %>% 
        body_add_par("Hello World") %>%
        print(target = file)
}


# For button 3
gen_docx2 <- function(file, doc) {
    file %>% 
        body_add_par("Hello World") %>%
        body_add_docx(src = doc) %>%
        print(target = file)
}




ui <- fluidPage(
    titlePanel("Example"),

    downloadButton("chart", "Get Chart"),
    downloadButton("document", "Get New Doc"),
    downloadButton("document2", "Get Doc from Template"), 
    tags$hr(),
    tags$p("Example hyperlink works"),
    tags$a(href='template.docx', target='_blank', 'Can only download from www folder', download = 'template.docx')

)

server <- function(input, output) {

    output$chart <- downloadHandler(
        filename = function() paste0("chart_", Sys.Date(), ".pptx"),

        content = function(file) {
            file_pptx <- tempfile(fileext = ".pptx")
            gen_pptx(chart, file_pptx)
            file.rename( from = file_pptx, to = file )
        }
    )

    output$document <- downloadHandler(
        filename = function() paste0("doc_", Sys.Date(), ".docx"),

        content = function(file) {
            file_docx <- tempfile(fileext = ".docx")
            gen_docx(file_docx)
            file.rename( from = file_docx, to = file )
        }
    )

    output$document2 <- downloadHandler(
        filename = function() paste0("doc_", Sys.Date(), ".docx"),

        content = function(file) {
            file_docx <- tempfile(fileext = ".docx")
            gen_docx2(file_docx, mytemplate)
            file.rename( from = file_docx, to = file )

        }
    )


}


shinyApp(ui = ui, server = server)

Upvotes: 2

Views: 1529

Answers (1)

Karol Daniluk
Karol Daniluk

Reputation: 544

Do not use print to generate docx file in helper function gen_docx2. Instead, you should use print as a last step in content function to return file to the downloadHandler.

Simple example:

gen_docx2 <- function(file, doc) {
    file %>% 
        body_add_par("Hello World") %>%
        body_add_docx(src = doc)
}

notice the function above does not print anything

outputdocument2 <- downloadHandler(
  filename = function() {
    paste0("doc_", Sys.Date(), ".docx")
  },
  content = function(file) {
    doc <- read_docx() %>% gen_docx2(file_docx, mytemplate)
    print(doc, target = file)
  }
)

Use read_docx() to generate temporary word file and then use print(doc, target = file) in the end to pass it to the downloadHandler. The function file.rename is not needed.

Upvotes: 0

Related Questions