Estoy trabajando en una aplicación brillante donde el usuario cargará un archivo de Excel, los datos se manipularán y luego se exportará un nuevo archivo de Excel con estos datos para que el usuario los examine. Tengo problemas con la función downloadHandler. Solía crear un archivo de Excel completamente nuevo cada vez en función de los datos cargados como este:

output$export <- downloadHandler(
filename = "answers.xlsx",
content = function(file){
  write.xlsx(exportdata(), file)
     })
  })

Esto funciona bien

Ahora me gustaría editar un archivo de Excel que incluiré cuando publique la aplicación y permitiré al usuario descargar esta versión editada como esta:

output$export <- downloadHandler(
filename = "answers.xlsx",
content = function(file){
  wb <- loadWorkbook("6rep-charts.xlsx")
  writeData(wb, sheet = "Species Match Results", correlInput())
  writeData(wb, sheet = "BS1 Data", bs1Input())
  writeData(wb, sheet = "BS2 Data", bs2Input())
  saveWorkbook(wb, file)
})

Sin embargo, esto da como resultado el error Warning: Error in write_file: Expecting a single string value: [type=character; extent=0]. [No stack trace available]. No estoy seguro de qué va mal, ya que cuando ejecuto la sección de contenido fuera de la aplicación brillante, funciona bien. El problema parece estar en el comando saveWorkbook.

La razón por la que me gustaría editar un archivo de Excel existente en lugar de crear uno nuevo es que el archivo de plantilla que incluyo en la aplicación ya tiene gráficos que deberían cambiar cuando se escriben los nuevos datos en el archivo. A los usuarios les gustaría poder editar estos gráficos por sí mismos, en lugar de solo ver una imagen de un gráfico. Si alguien tiene una forma más sencilla de lograr esto, ¡sería genial! ¡Gracias de antemano por su ayuda!

Ejemplo reproducible usando este archivo de Excel:

    library(shiny); library(readxl); library(xlsx)
    ui <- shinyUI(fluidPage(

    titlePanel("Old Faithful Geyser Data"),
    fluidRow(
       column(3,
          downloadButton(outputId = "export",
               label = "Export Results to Excel")
  ),

       column(6,
         dataTableOutput("data")
  ))))

    server <- function(input, output) {
       adata <- faithful[1:20,]   
       bdata <- faithful[21:50,]   
       cdata <- faithful[51:200,]

   read_excel_allsheets <- function(filename, tibble = FALSE) {
      sheets <- readxl::excel_sheets(filename)
      x <- lapply(sheets, function(Y) {readxl::read_excel(filename, sheet = Y)})
      if(!tibble) x <- lapply(x, as.data.frame)
      names(x) <- sheets
      x
   }

   output$data <- renderDataTable({
       adata   })

   output$export <- downloadHandler(
       filename = "answers.xlsx",
       content = function(file){
          wb <- loadWorkbook("./Data/template.xlsx")
          writeData(wb, sheet = "Alpha", adata)
          writeData(wb, sheet = "Beta", bdata)
          writeData(wb, sheet = "Gamma", cdata)
          saveWorkbook(wb, file="./Data/temp.xlsx", overwrite = T)
          print("done")
          Fin_WB<- read_excel_allsheets("./Data/temp.xlsx")
          write.xlsx(Fin_WB, file)
        }   ) }

   shinyApp(ui = ui, server = server)
1
Zoe Bleicher 16 oct. 2018 a las 23:20

2 respuestas

La mejor respuesta

A la función saveWorkbook no le gusta operar como una función de escritura para la función content. Confuso, sí, pero aún podemos usarlo dentro de la función de contenido, pero no como el paso final. Tenemos que usar una función de escritura clásica como write.xlsx para satisfacer la función de contenido que espera que el archivo y la ruta del archivo escriban.

Para solucionar esto, creamos un archivo temporal en la carpeta de datos (esta carpeta estará en el directorio con el servidor y la interfaz de usuario, y se publicará). El programa lee el archivo de plantilla (consulte la función especial a continuación), lo modifica y luego escribe / sobrescribe el archivo temp . Este archivo temporal es luego leído y asignado por read.xlsx y luego escrito por write.xlsx. Esto satisface la función de contenido y nos permite utilizar una plantilla para exportar.

read_excel_allsheets <- function(filename, tibble = FALSE) {
    sheets <- readxl::excel_sheets(filename)
    x <- lapply(sheets, function(X) readxl::read_excel(filename, sheet = X))
    if(!tibble) x <- lapply(x, as.data.frame)
    names(x) <- sheets
    x
  }

output$export <- downloadHandler(
    filename = "answers.xlsx",
    content = function(file){
      wb <- loadWorkbook("./Data/template.xlsx")
      writeData(wb, sheet = "Alpha", adata)
      writeData(wb, sheet = "Beta", bdata)
      writeData(wb, sheet = "Gamma", cdata)
      saveWorkbook(wb, file="./Data/temp.xlsx", overwrite = T)
      print("done")
      Fin_WB<- read_excel_allsheets("./Data/temp.xlsx")
      write.xlsx(Fin_WB, file)
    }) 
  }

Para leer en todas las hojas, no podemos simplemente usar read.xlsx ya que no verá todas las hojas. Usando una función creada aquí de Jeromy Anglim, podemos leer todas las hojas de Excel. Esta es la función read_excel_allsheets.

0
Chabo 17 oct. 2018 a las 16:40

Solución más simple (no puedo dejar comentarios debido a una reputación demasiado baja):

output$export <- downloadHandler(
filename = "answers.xlsx",
content = function(file){
  wb <- loadWorkbook("./Data/template.xlsx")
  writeData(wb, sheet = "Alpha", adata)
  writeData(wb, sheet = "Beta", bdata)
  writeData(wb, sheet = "Gamma", cdata)
  saveWorkbook(wb, file="./Data/temp.xlsx", overwrite = T)
  file.copy(from = "./Data/temp.xlsx", to = file)
}) 

No es necesaria la función read_excel_allsheets .

0
Christian Savemark 13 mar. 2020 a las 12:13