downloadHandler:在内容函数

时间:2017-08-02 10:31:54

标签: r shiny

是否可以在downloadHandler的内容部分中设置reactiveValues?我试过了,不理解这种行为。

一个简单的例子可能是一个计数器,显示点击下载按钮的频率:

library(shiny)
ui <- fluidPage(
  downloadButton("downloadData", "Download"),
  textOutput("nDownloads"),
  actionButton("trig", "get number")
)

server <- function(input, output) {
  # Our dataset
  data <- mtcars
  r.nDownloads <- reactiveValues(n=0)
  output$nDownloads <- renderText({
    input$trig
    paste("number of downloads:", r.nDownloads$n)
  })

  output$downloadData <- downloadHandler(
    filename = function() {
      paste("data-", Sys.Date(), ".csv", sep="")
    },
    content = function(file) {
      r.nDownloads$n <- r.nDownloads$n + 1
      write.csv(data, file)
    }
  )
}

shinyApp(ui, server)

如果单击下载按钮,textOutput将显示为灰色,但不会更新。我添加了一个操作按钮作为触发器来强制更新renderText。令人惊讶的是(至少对我来说)有效:显示正确的数字。

因此,downloadHandler会以某种方式更改reactiveValue,但其依赖关系只会失效,而不会更新。

当然,正确的做法是将“数据” - 对象反应并在那里进行计数。但我很好奇如何解释所描述的行为。

修改

好的,现在我真的很困惑:我尝试了上面提到的内容:让“数据”反应并在那里进行计数。这不能简单地计算下载量,因为如果数据无效,则只会重新计算。

以下是一个示例,其中包含“数据”无效的附加输入:

library(shiny)

ui <- fluidPage(
  numericInput("nRows", label = "nRows", min=1, max=32, value=15),
  downloadButton("downloadData", "Download"),
  textOutput("nDownloads")
)

server <- function(input, output) {
  r.nDownloads <- reactiveValues(n=0)
  # Our dataset
  data <- reactive({
    isolate({
      r.nDownloads$n <- r.nDownloads$n + 1
    })
    mtcars[1:input$nRows,]
  })
  output$nDownloads <- renderText({
    paste("number of downloads:", r.nDownloads$n)
  })

  output$downloadData <- downloadHandler(
    filename = function() {
      paste("data-", Sys.Date(), ".csv", sep="")
    },
    content = function(file) {
      write.csv(data(), file)
    }
  )
}

shinyApp(ui, server)

但是,我仍然看到了类似的行为:单击下载按钮会将文本显示为灰色,更改“nRows”会产生预期的下载次数(现在在nRows更改后下载; - ))出现。

现在它给我带来了实际问题:在我的真实应用程序中,可以下载相当复杂的Excel文件。在准备和格式化Excel文件时,可能会发生应该导致应用程序反应的事件。这就是下载应该触发的原因。我可以看到的替代方法是,在用户点击下载之前准备Excel文件(我想避免的,因为这可能需要几秒钟,具体取决于文件/格式的复杂性)

我错过了一些明显的东西吗?如果没有,我会很感激任何想法,下载事件如何在应用程序的其余部分触发某些内容。

1 个答案:

答案 0 :(得分:0)

解决方案是删除reactiveValues的隔离,因为这会阻止在触发numericInput之前更新计数器。这是因为data()依赖于input$nrows

library(shiny)

ui <- fluidPage(
  numericInput("nRows", label = "nRows", min = 1, max = 32, value = 15),
  downloadButton("downloadData", "Download"),
  textOutput("nDownloads")
)

server <- function(input, output) {
  r.nDownloads <- reactiveValues(n = 0)
  # Our dataset
  data <- reactive({
    r.nDownloads$n <- r.nDownloads$n + 1
    mtcars[1:input$nRows,]
  })

  output$nDownloads <- renderText({
    paste("number of downloads:", r.nDownloads$n)
  })

  output$downloadData <- downloadHandler(
    filename = function() {
      paste("data-", Sys.Date(), ".csv", sep = "")
    },
    content = function(file) {
      write.csv(data(), file)
    }
  )
}

shinyApp(ui, server)

关于更深层次的问题,如果不能保证用户将下载该文件,则不断准备一个复杂的Excel文件效率低下。您可以尝试做的是:

  1. 使用reactive方法(例如data())保存数据。
  2. 编写一种方法来准备要下载的数据(例如prepExcel(data)),以返回准备好的数据。
  3. 将(1)和(2)传递到downloadHandler()的内容中,如下所示:write_xx(prepExcel(data()))或将数据通过管道传递到write_xx函数中,例如data() %>% prepExcel() %>% write_xx(),其中{ {1}}是用于输出最终文件的方法,例如xxwrite_xlsx等。

我希望这会有所帮助。