【问题标题】:Is it possible to change downloading behavior for large PNG files?是否可以更改大型 PNG 文件的下载行为?
【发布时间】:2022-01-05 15:16:18
【问题描述】:

在我的 Shiny 应用程序中,我制作了一个非常沉重的情节。当我想下载这个情节时,R首先在后台生成PNG文件,然后打开文件系统选择我要保存的位置。

问题是点击下载按钮后,情节创建需要一些时间,因此用户不知道它是否有效。

下面的例子:情节有点沉重,所以需要一些时间才能出现。等待它出现后再点击“下载”按钮。

library(shiny)
library(ggplot2)

foo <- data.frame(
  x = sample(seq(1, 20, by = 0.01), 5*1e5, replace = TRUE),
  y = sample(seq(1, 20, by = 0.01), 5*1e5, replace = TRUE)
)

ui <- fluidPage(
  downloadButton('foo'),
  plotOutput("test")
)

server <- function(input, output) {
  output$test <- renderPlot(ggplot(foo, aes(x, y)) + geom_point())
  output$foo = downloadHandler(
    filename = 'test.png',
    content = function(file) {
      ggsave(file)
    }
  )
}

shinyApp(ui, server)

有没有办法反转这个过程,即首先让用户选择保存绘图的位置,然后在后台生成 PNG?我认为这将提供更好的用户体验。

【问题讨论】:

    标签: r shiny


    【解决方案1】:

    关于您在@manro's answer 下方的评论:承诺在这里无济于事。

    它们正在防止其他闪亮的会话被繁忙的会话阻止。它们增加了会话间响应而不是会话内响应 - although there are (potentially dangerous) workarounds。

    查看此答案进行测试: R Shiny: async downloadHandler

    最后,downloadButton 只是提供了一个带有download attribute 的链接(a-tag)。

    如果客户端尝试访问链接资源时不存在,浏览器将抛出错误(就像在 MRE 中准备好绘图之前单击 downloadButton 时一样)。 此外,提供文件路径的对话框在单击链接后由客户端浏览器执行(而不是由 R)。

    我认为以某种方式通知用户是你所能做的:

    library(shiny)
    library(ggplot2)
    
    foo <- data.frame(
      x = sample(seq(1, 20, by = 0.01), 1e5, replace = TRUE),
      y = sample(seq(1, 20, by = 0.01), 1e5, replace = TRUE)
    )
    
    ui <- fluidPage(
      tags$br(),
      conditionalPanel(condition = 'output.test == null', tags$b("Generating plot...")),
      conditionalPanel(condition = 'output.test != null', downloadButton('foo'), style = "display: none;"),
      plotOutput("test")
    )
    
    server <- function(input, output, session) {
    
      output$test <- renderPlot(ggplot(foo, aes(x, y)) + geom_point())
      output$foo = downloadHandler(
        filename = 'test.png',
        content = function(file) {
          showNotification(
            ui = tags$b("Preparing download..."),
            duration = NULL,
            closeButton = TRUE,
            id = "download_notification",
            type = "message",
            session = session
          )
          ggsave(file)
          removeNotification(id = "download_notification", session = session)
        }
      )
    }
    
    shinyApp(ui, server)
    

    【讨论】:

    • 好吧,我猜在要保存的文件存在之前浏览器是打不开文件系统的吧?
    • @bretauv 完全正确。
    【解决方案2】:

    这是我的第一个闪亮的应用程序,所以我相信它可以改进;)

    我认为,从用户体验的角度来看 - 最好按照以下方式进行:“显示图表 -> 保存图表”


    补充:

    所以,我添加了一个忙碌的微调器,现在这个应用程序的用户可以知道这个图表仍在渲染。你可以使用多种风格,选择你喜欢的there:


    library(shiny)
    library(ggplot2)
    library(shinybusy)
    
    #your data  
    df <- data.frame(
      x <- sample(seq(1, 20, by = 0.01), 5*1e5, replace = TRUE),
      y <- sample(seq(1, 20, by = 0.01), 5*1e5, replace = TRUE)
    )
    
    #your plot
    plot_df <- ggplot(df, aes(x, y)) + geom_point() 
    
    #my plot 
    my_plot <- ggplot(diamonds, aes(price, fill = cut)) +
                  geom_histogram(binwidth = 500)
    
    ui <- fluidPage(
          #our buttons
          br(),
          actionButton("button1", label = "View graph"),
          br(),
          br(),
          plotOutput("button1"),
          uiOutput("button2"),
          add_busy_spinner(spin = "fading-circle")      
          )
    
    server <- function(input, output) {
                  
           observeEvent(input$button1, {
             output$button1 <- renderPlot(my_plot)
             output$button2 <- renderUI({
               br()               
               downloadButton("button3")
             })
           })
           
           output$button3 <- downloadHandler(
              filename <- 'test.png',
              content <- function(file){
                        ggsave(file)
              }
           )
    }
    
    shinyApp(ui, server)
    

    【讨论】:

    • 是的,最好先显示图表然后保存它,但这不是我的问题中描述的问题(或者我没有很好地描述它)。我的问题是我的情节很重(这就是为什么它没有直接出现在应用程序的开头),当我在它出现后按“下载”时,图表首先转换为PNG然后下载。问题是转换为 PNG 文件需要很长时间,因此用户看不到任何事情发生。所以我的问题是:有没有办法在创建 PNG 之前选择保存绘图的位置?我会编辑
    • @bretauv 您可以添加一个文本标签,例如(请稍等 - 情节正在渲染)。我想,这就够了。
    • @bretauv 寻找补充 ;)
    • @bretauv 抱歉,library(promises) 无法帮助您。正如我之前所说,这是我的第一个 Shiny App :) 我还需要学习很多东西。
    • 好的,谢谢你的努力
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-25
    • 2017-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多