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

Kyl*_*ise 4 r shiny

SO 上有许多关于此主题的不同问答,但我找不到适合我的用例的问答。我也感到非常惊讶的是,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)
Run Code Online (Sandbox Code Playgroud)

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

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

我觉得我已经非常接近了,但是我还没有能够成功地完成这项工作。有任何想法吗?

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

And*_*ter 7

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

  • 使用dir而不是在调用ls内部zip::zip显示临时目录的内容(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)
Run Code Online (Sandbox Code Playgroud)

在我自己的 Shiny 应用程序中,我使用了您上面建议的多工作表方法。用于生成多页 xlsx 工作簿的替代设置openxlsx可能是:

...

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

...
Run Code Online (Sandbox Code Playgroud)

由reprex 包于 2021 年 12 月 16 日创建(v2.0.1)

  • 只是出于好奇,安迪,您是否知道如何将其重构为不需要“zip”?即,一次只下载 3 个文件? (2认同)