如何从 R/Shiny 应用程序下载多个文件?

Posted

技术标签:

【中文标题】如何从 R/Shiny 应用程序下载多个文件?【英文标题】:How to download multiple files from R/Shiny app? 【发布时间】:2022-01-19 23:10:49 【问题描述】:

关于 SO 的这个主题有许多不同的 Q/A,但我找不到适合我用例的。我也很惊讶 RStudio / Shiny 开发人员自己没有提供一些关于如何做到这一点的文档。无论如何,以这个示例应用程序为例:

library(shiny)
library(glue)
library(tidyverse)

# Define UI for application 
ui <- fluidPage(
    # Application title
    titlePanel("Test Multi-File Download"),
    p("I hope this works!"),
    downloadButton(
        outputId = "download_btn",
        label = "Download",
        icon = icon("file-download")
    )
)

# Define server logic 
server <- function(input, output) 
    
    #datasets stored in reactiveValues list
    to_download <- reactiveValues(dataset1 = iris, dataset2 = airquality, dataset3 = mtcars, dataset4 = NULL)
    blahblah <- iris
    
    output$download_btn <- downloadHandler(
        filename = function()
            paste("my_data_", Sys.Date(), ".csv", sep = "")
        ,
        content = function(file)
            
           #works
           #readr::write_csv(blahblah, file)
 
                
            #Attempt 1
           #  #create some temp directory
           #  temp_directory <- tempdir()
           # browser()
           #   reactiveValuesToList(to_download) %>%
           #       #x is data, y is name
           #      imap(function(x,y)
           #          #browser()
           #          #check if data is not null
           #          if(!is.null(x))
           #              #create file name based on name of dataset
           #              file_name <- glue("y_data.csv")
           #              #write file to temp directory
           #              readr::write_csv(x, file_name)
           #          
           #      )
            
            # zip::zip(
            #     zipfile = file,
            #     files = ls(temp_directory),
            #     root = temp_directory
            # )

        
        
    )

    


# Run the application 
shinyApp(ui = ui, server = server)

我有一些数据集存储在reactiveValues 列表中,我希望用户能够下载它们。理想情况下,我希望他们能够一次下载多个文件,而不是必须zip 他们,然后下载.zip 文件。我可以接受的另一个选择是将每个数据集添加到 Excel 工作表,然后下载多工作表 Excel 文件。 我的一般思考过程(关于前者)如下:

    下载按钮被按下 创建一些临时目录 将to_download reactiveValues 列表中包含的(不是NULL)数据集写入此目录 zip临时目录并下载

我觉得我很接近,但是我还没有能够成功地完成这项工作。有什么想法吗?

编辑 1:我知道建议的答案 here,但想避免使用 setwd(),因为我认为在 Shiny 应用程序中弄乱工作目录是不好的做法。

【问题讨论】:

这能回答你的问题吗? Download multiple csv files with one button (downloadhandler) with R Shiny @NelsonGon 我已经阅读了这个问题,在我看来,在 Shiny 应用程序中使用 setwd() 似乎(非常)不好的做法。如果可能的话,我想避免这种情况。 对于 multi_excel(啊,我被困在 tidyverse_syntax)表中,我认为您可以在按钮单击时循环通过 reactiveVals 并使用 openxl(sx) 或其他一些 excel 编写器写入表。 @NelsonGon 是的,我已经尝试过(使用writexl::write_xlsx()),其方式与我在imap() 中的注释方式类似,但未能成功提供一些条件来测试是否数据是否为NULL 【参考方案1】:

编辑了一些东西,它正在工作:

zip::zip调用中使用dir而不是ls来显示临时目录的内容(ls列出了R环境而不是目录内容) 作为进一步的建议:在 tempdir() 内创建一个新的唯一文件夹,以确保仅添加相关文件。
library(shiny)
library(glue)
library(tidyverse)

# Define UI for application 
ui <- fluidPage(
  # Application title
  titlePanel("Test Multi-File Download"),
  p("I hope this works!"),
  downloadButton(
    outputId = "download_btn",
    label = "Download",
    icon = icon("file-download")
  )
)

# Define server logic 
server <- function(input, output) 
  
  #datasets stored in reactiveValues list
  to_download <- reactiveValues(dataset1 = iris, dataset2 = airquality, dataset3 = mtcars, dataset4 = NULL)
  blahblah <- iris
  
  output$download_btn <- downloadHandler(
    filename = function()
      paste("my_data_", Sys.Date(), ".zip", sep = "")
    ,
    content = function(file)
      
       temp_directory <- file.path(tempdir(), as.integer(Sys.time()))
       dir.create(temp_directory)
       
        reactiveValuesToList(to_download) %>%
           imap(function(x,y)
               if(!is.null(x))
                   file_name <- glue("y_data.csv")
                   readr::write_csv(x, file.path(temp_directory, file_name))
               
           )
        
      
      zip::zip(
          zipfile = file,
          files = dir(temp_directory),
          root = temp_directory
      )
      
      

    ,
    contentType = "application/zip"
    
  )
  
  


shinyApp(ui = ui, server = server)

In my own Shiny app 正如您上面建议的那样,我使用了多工作表方法。使用openxlsx 生成多页 xlsx 工作簿的替代设置可能是:

...

output$download_btn <- downloadHandler(
    filename = function()
      paste("my_data_", Sys.Date(), ".xlsx", sep = "")
    ,
    content = function(file)
      
       wb <- createWorkbook()
       
        reactiveValuesToList(to_download) %>%
           imap(function(x,y)
               if(!is.null(x))
                 addWorksheet(wb, sheetName = y)
                 writeData(wb, x, sheet = y)
               
           )
        
        saveWorkbook(wb, file = file)
    ,
    contentType = "file/xlsx"
    
  )

...

由reprex package 创建于 2021-12-16 (v2.0.1)

【讨论】:

感谢您的帮助,安迪!我很感激。 只是出于好奇,安迪,您对如何将其重构为不需要 zip 有任何见解吗?即,一次只下载 3 个文件? 嗯,这是一个很好的问题,我认为超出了正常闪亮工具的范围(download handler takes one file)。您也许可以手动制作一个按钮以通过一些javascript 提示它?我想知道这样做是否有点不礼貌:P FWIW - 我非常喜欢你的 zip 方法,可能会记下它作为未来应用程序的工具! 是的,同意。 zip 就是这样!再次感谢您的帮助,并感谢您的称赞。

以上是关于如何从 R/Shiny 应用程序下载多个文件?的主要内容,如果未能解决你的问题,请参考以下文章

R Shiny Pass 文件列表到 Javascript 下载器

R Shiny:如何从数据表中的自定义按钮调用 JavaScript 函数

从 selectInput 到 facet_wrap 的多个变量:R Shiny

如何使用具有多个输出的 R Shiny moduleServer

R Shiny:如何使多个元素在添加/删除按钮上下文中具有反应性?

从 R Shiny App 输出纯 HTML 文件