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

4

关于这个主题,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文件。我的具体思路(前者)如下:
  1. 按下下载按钮
  2. 创建一个临时目录
  3. 将包含在to_download reactiveValues列表中的(非NULL)数据集写入此目录
  4. zip这个临时目录并下载
我感觉我已经很接近了,但我还没有能够成功地实现这个功能。有什么建议吗? 编辑1: 我知道这里提出的答案, 但我想避免使用setwd(),因为我认为从Shiny应用程序内部更改工作目录是不良做法。

这个回答解决了你的问题吗?使用R Shiny的downloadhandler一键下载多个csv文件 - NelsonGon
2
@NelsonGon 我已经阅读了这个问题,对我来说在Shiny应用程序中使用setwd()是非常不好的做法。如果可能的话,我想尽量避免这种情况。 - Kyle Weise
对于 multi_excel(啊,我被 tidyverse_syntax 卡住了)表格,我认为你可以在按钮点击时循环遍历 reactiveVals,并使用 openxl(sx) 或其他 Excel 写入器写入表格。 - NelsonGon
@NelsonGon 是的,我尝试过使用writexl::write_xlsx(),类似于我已经注释掉的方式,在imap()内部进行操作,但是在提供一些条件来测试数据是否为NULL时没有成功。 - Kyle Weise
1个回答

6

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

  • 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)

在我自己的Shiny应用程序中,我采用了你上面建议的多工作表方法。使用openxlsx,另一种可行的设置可以生成一个多工作表的xlsx工作簿:

https://github.com/andrewbaxter439/ITS_shinyapp/blob/master/server.R#L127

...

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包(v2.0.1)于2021年12月16日创建。


1
感谢你的帮助,Andy!我很感激。 - Kyle Weise
2
只是出于好奇,安迪,你有没有什么见解,可以重构这个代码,而不需要使用zip?也就是说,一次只下载3个文件? - Kyle Weise
1
嗯,这是一个很好的问题,我认为超出了正常闪亮工具的范围(下载处理程序只能接受一个文件)。也许您可以手动制作一个按钮,并通过一些 JavaScript 来提示它?但我想这样做可能有点不礼貌:P - Andy Baxter
顺便说一句 - 我非常喜欢你这里的“zip”方法,可能会把它作为未来应用程序的工具做个心理记录! - Andy Baxter
1
没错,同意使用 zip!再次感谢您的帮助,并感谢您的赞美。 - Kyle Weise

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接