【问题标题】:Changing the size and storing an image file uploaded by the user with Shiny使用 Shiny 更改大小并存储用户上传的图像文件
【发布时间】:2021-09-06 12:54:23
【问题描述】:

我正在编写一个大型 Shiny Dashboard 脚本来从用户上传的文件中收集数据。其中一些文件是图像。该脚本通过ui 会话中的fileInput 获取文件。通常,用户上传高分辨率图像,但我不需要存储此类文件,因此脚本将大小减小到 height = 200 以便将其定向到outputImage。它将文件发送到 Google Drive(没问题),但我想发送低分辨率文件。我试图从output$showphotos1 阅读它们,但是 部分脚本:

    ui <- dashboardPage(
               fileInput("loadphotos", label="Carregar fotos", multiple=T),
                          actionButton("do", "Carregar"),
                          imageOutput("showphotos1", height="200px"),
                          imageOutput("showphotos2", height="200px"),
                          imageOutput("showphotos3", height="200px")
    )
    
    server <- function(input, output, session) {
      observeEvent(input$do, {
        lst <- NULL
        for(i in 1:length(input$loadphotos[,1])) {
          lst[[i]] <- input$loadphotos[[i, 'datapath']]
        }
        output$showphotos1 <- renderImage({list(src=lst[[1]], height="200")})
        output$showphotos2 <- renderImage({list(src=lst[[2]], height="200")})
        output$showphotos3 <- renderImage({list(src=lst[[3]], height="200")})

#        drive_upload(output$showphotos1$datapath, 
#               as_id("https://drive.google.com/drive/u/1/folders/1qj0eeee...")
#       This gives an error: "Error in $.shinyoutput: Reading from shinyoutput object     
#       is not allowed." So I used the lines bellow, that uploads large files from     
#       the input:

         drive_upload(input$loadphotos,
                as_id("https://drive.google.com/drive/u/1/folders/1qj0eeee...")
        })
    }

我想存储 output$showphotos 中的较小文件 (200px),而不是 input@loadphotos 中的较大文件。我不精通R,如果有人能给我简单的解决方案,我将不胜感激。也欢迎提出避免每个图像文件重复代码的建议。

【问题讨论】:

    标签: r image shiny upload storage


    【解决方案1】:

    我们可以使用magick::image_scale() 调整图像大小,然后将它们保存在工作目录中(或者可能创建临时文件),因为drive_update 将路径作为media 参数。

    避免代码重复的版本:

    library(shiny)
    library(magick)
    library(tidyverse)
    library(googledrive)
    
    n_showphotos <- 3
    
    ui <- fluidPage(
        fileInput("loadphotos", label = "Carregar fotos", multiple = TRUE),
        actionButton("do", "Carregar"),
        tagList(
        map(str_c('showphotos', 1:n_showphotos), ~imageOutput(.x, height = '200px')))
    )
    
    server <- function(input, output, session) {
        
        observeEvent(input$do, {
            
    
            lst <- NULL
            for (i in 1:length(input$loadphotos[,1])) {
                lst[[i]] <- input$loadphotos[[i, 'datapath']]
            }
            
            lst %>%
                map2(str_c('showphotos', 1:length(.)),~ { output[[.y]] <- renderImage({list(src = .x, height="200")},deleteFile = FALSE) })
            
            #a list with all the images but resized to 200
            #"x200" to resize by height
            images_resized <- lst %>% 
                map(~image_scale(image = image_read(.x), "200"))
            
            #images will be located in the project directory or home folder (getwd() to get working directory if in doubt)
            images_resized %>%
                walk2(str_c('image', 1:length(.)), ~ image_write(.x, path = str_c(.y, '.png'), format = "png"))
            
            
            #        drive_upload(image1.png, 
            #               as_id("https://drive.google.com/drive/u/1/folders/1qj0eeee...")
       
        })
        
    }
    
    shinyApp(ui, server)
    

    代码重复:

    library(tidyverse)
    library(googledrive)
    library(shiny)
    library(magick)
    
    ui <- fluidPage(
        fileInput("loadphotos", label="Carregar fotos", multiple=T),
        actionButton("do", "Carregar"),
        imageOutput("showphotos1", height="200px"),
        imageOutput("showphotos2", height="200px"),
        imageOutput("showphotos3", height="200px")
    )
    
    server <- function(input, output, session) {
        observeEvent(input$do, {
            lst <- NULL
            req(input$loadphotos)
            for(i in 1:length(input$loadphotos[,1])) {
                lst[[i]] <- input$loadphotos[[i, 'datapath']]
            }
            output$showphotos1 <- renderImage({list(src=lst[[1]], height="200")})
            output$showphotos2 <- renderImage({list(src=lst[[2]], height="200")})
            output$showphotos3 <- renderImage({list(src=lst[[3]], height="200")})
            
            
            images_resized <- NULL
            for (i in 1:length(lst)) {
                
               image_scale(image = image_read(lst[[i]]), '200') %>% 
               image_write(path = str_c('image', i, '.png'), format = "png")
                
            }
            
            #image1.png ... image3.png are available in the working directory.
            
            #        drive_upload(image1, 
            #               as_id("https://drive.google.com/drive/u/1/folders/1qj0eeee...")
    
            
            })
    }
    
    shinyApp(ui, server)
    

    编辑: 根据用户上传的图片数量调整ui。

    library(shiny)
    library(magick)
    library(tidyverse)
    library(googledrive)
    
    ui <- fluidPage(
        fileInput("loadphotos", label = "Carregar fotos", multiple = TRUE),
        actionButton("do", "Carregar"),
        uiOutput('images_outputs')
    )
    
    server <- function(input, output, session) {
        
        observeEvent(input$do, {
            
            lst <- NULL
            for (i in 1:length(input$loadphotos[,1])) {
                lst[[i]] <- input$loadphotos[[i, 'datapath']]
            }
            
            output$images_outputs <- renderUI({
                tagList(
                    map(str_c('showphotos', 1:length(lst)), ~imageOutput(.x, height = '200px')))
            })
            
            lst %>%
                map2(str_c('showphotos', 1:length(.)),~ { output[[.y]] <- renderImage({list(src = .x, height="200")},deleteFile = FALSE) })
            
            #a list with all the images but resized to 200
            #"x200" to resize by height
            images_resized <- lst %>% 
                map(~image_scale(image = image_read(.x), "200"))
            
            #images will be located in the project directory or home folder (getwd() to get working directory if in doubt)
            images_resized %>%
                walk2(str_c('image', 1:length(.)), ~ image_write(.x, path = str_c(.y, '.png'), format = "png"))
            
            
            #        drive_upload(image1.png, 
            #               as_id("https://drive.google.com/drive/u/1/folders/1qj0eeee...")
            
        })
        
    }
    
    shinyApp(ui, server)
    

    注意:可能需要使用options(shiny.maxRequestSize={size}) 调整shiny 接受的最大文件大小,如here 所示

    【讨论】:

    • 天啊,因为捷径,我不得不学习一下,但这是一个非常好的解决方案。感谢您添加两个版本的代码,因此我也可以比较它们以了解快捷方式。 “二兔一击!” ;)
    • jpdugo17,我还有一个问题。告诉我是否最好将其作为新问题打开。如果我希望用户设置上传的图像数量?我的意思是,它不是n_showphotos &lt;- 3,而是由用户上传的图像数量设置的。我不能这样做,因为 input$loadphotos 只是在用户上传图像后设置。 R 给我一个错误“找不到对象输入”。
    • 也许是的,这个问题主要是关于减小图像大小。要根据用户输入动态更改显示的图像数量,需要稍微重构应用程序。此外,可以使用renderUIinsertUI 找到多种解决方案。
    猜你喜欢
    • 2013-08-04
    • 1970-01-01
    • 2016-11-18
    • 2016-12-08
    • 2016-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多