【问题标题】:Render image before uploading it in Shiny在 Shiny 中上传之前渲染图像
【发布时间】:2017-06-26 12:06:24
【问题描述】:

我正在构建一个 Shiny 应用程序,让用户可以将图像上传到服务器。我想在屏幕上显示图像,而不必先上传它,然后再取回渲染的输出。这可能吗?

这是我现在的代码。您可以选择上传的图像文件。然后在接收到图像后,从服务器端的文件中渲染图像。我想避免往返。

用户界面

fluidPage(
    titlePanel("File upload"),
    sidebarLayout(
        sidebarPanel(
            fileInput("img", "Choose image file",
                accept=c("image/jpeg", "image/x-windows-bmp"))
        ),
        mainPanel(
            imageOutput("picture", width="500px", height="500px")
        )
    )
)

服务器

function(input, output, session)
{
    output$picture <- renderImage({
        imgFile <- input$img
        if(is.null(imgFile))
            return(list(src=""))
        list(src=imgFile$datapath, alt=imgFile$name, contentType=imgFile$type)
    }, deleteFile=FALSE)

    # do more stuff with the file
}

【问题讨论】:

  • 我不确定我是否理解这个问题。该文件在您用户的计算机上而不是在您的服务器上,您想在不上传的情况下显示它?有没有shiny外的上传选项,你知道路径吗?
  • 我想上传文件,但我也想在不等待服务器回复我的情况下显示它。我做了一些可能需要一些时间的后端计算,所以能够立即显示图像会很好。

标签: javascript r shiny shinyjs


【解决方案1】:

您可以使用包 shinyjs 从 HTML 5 read here 调用 FileReader

library(shinyjs)
shinyApp(ui = fluidPage(
  useShinyjs(),
  titlePanel("File upload"),
  sidebarLayout(
    sidebarPanel(
      fileInput("img", "Choose image file",
                accept=c("image/jpeg", "image/x-windows-bmp")),
      HTML('<output id="list"></output>')
    ),
    mainPanel(
      imageOutput("picture", width="500px", height="500px")
    )
  )), 
server = function(input, output, session){ 
  shinyjs::runjs("

  function handleFileSelect(evt) {
   var files = evt.target.files; // FileList object
   // Loop through the FileList and render image files as thumbnails.
   for (var i = 0, f; f = files[i]; i++) {

   // Only process image files.
   if (!f.type.match('image.*')) {
   continue;
   }

   var reader = new FileReader();

   // Closure to capture the file information.
   reader.onload = (function(theFile) {
   return function(e) {
   // Render thumbnail.
   var span = document.createElement('span');
   span.innerHTML = ['<img class=\"thumb\" src=\"', e.target.result,
   '\" title=\"', escape(theFile.name), '\"/>'].join('');
   document.getElementById('list').insertBefore(span, null);
   };
   })(f);

   // Read in the image file as a data URL.
   reader.readAsDataURL(f);
   }
   }
   document.getElementById('img').addEventListener('change', handleFileSelect, false);")

  output$picture <- renderImage({
    imgFile <- input$img
    if(is.null(imgFile))
      return(list(src=""))
    list(src=imgFile$datapath, alt=imgFile$name, contentType=imgFile$type)
    }, deleteFile=FALSE)
})

【讨论】:

  • 这很酷,但我遇到了一些问题。首先,JS生成的图片出现在侧边栏中,我希望它替换主面板中的图片。此外,如果我多次使用上传控件,侧边栏图片不会被删除。相反,每张新图片都显示在之前的图片下方。
  • 我可以通过将HTML() 元素放在主面板中来解决第一个问题,但我不确定如何解决第二个问题。
【解决方案2】:

编辑: 好的,我现在的问题对我来说很清楚,我希望:)。 问题是图片是在&lt;output id="list"&gt;&lt;/output&gt; 中添加的。所以我建议在添加新图片之前清除它:document.getElementById('list').innerHTML = ''

library(shiny)
library(shinyjs)
shinyApp(ui = fluidPage(
  useShinyjs(),
  titlePanel("File upload"),
  sidebarLayout(
    sidebarPanel(
      fileInput("img", "Choose image file",
                accept=c("image/jpeg", "image/x-windows-bmp"))
    ),
    mainPanel(
      HTML('<output id="list"></output>')
    )
  )), 
  server = function(input, output, session){ 
    shinyjs::runjs("

                   function handleFileSelect(evt) {
                   document.getElementById('list').innerHTML = ''
                   var files = evt.target.files; // FileList object
                   // Loop through the FileList and render image files as thumbnails.
                   for (var i = 0, f; f = files[i]; i++) {

                   // Only process image files.
                   if (!f.type.match('image.*')) {
                   continue;
                   }

                   var reader = new FileReader();

                   // Closure to capture the file information.
                   reader.onload = (function(theFile) {
                   return function(e) {
                   // Render thumbnail.
                   var span = document.createElement('span');
                   span.innerHTML = ['<img class=\"thumb\" src=\"', e.target.result,
                   '\" title=\"', escape(theFile.name), '\"/>'].join('');
                   document.getElementById('list').insertBefore(span, null);
                   };
                   })(f);

                   // Read in the image file as a data URL.
                   reader.readAsDataURL(f);
                   }
                   }
                   document.getElementById('img').addEventListener('change', handleFileSelect, false);")

  })

【讨论】:

  • 顺便说一句,要解决文件出现两次的问题,请删除 renderImage/imageOutput 位。
  • 我很困惑。当我运行上面提供的代码并多次上传时,我在侧边栏中没有看到图片,而在主面板中看到了一张图片,即我上次上传的那张(在 Firefox 和 Chrome 中).....
  • 发生的事情是您的 Javascript 根本没有产生任何输出。显示的图片来自renderImage调用,而不是JS代码。
  • 您可以通过在服务器代码中的某处插入Sys.sleep(2) 或通过更改由JS 创建的图像的尺寸来看到这一点。前者会延迟图片的渲染,后者则没有效果。
  • 我明白了,我删除了双显示,这有点令人困惑。我认为我的新编辑应该会有所帮助。
猜你喜欢
  • 2020-04-04
  • 1970-01-01
  • 2021-01-01
  • 2021-11-04
  • 2020-11-08
  • 2020-06-19
  • 2018-01-27
  • 2016-02-17
  • 2020-08-03
相关资源
最近更新 更多