Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uploading picture to the knitr document via Shiny

Tags:

r

knitr

shiny

I am using combination of Shiny and knitr to create PDF documents.

Currently I want to add feature that will allow user to upload picture that will be placed within the created document. However, I am really stuck because I am unable to get the path of the input picture. Could anyone help me with that?

Simple example:

Application:

library(knitr)
library(shiny)

ui <- fluidPage(

  sidebarLayout(
    sidebarPanel(
      fileInput("picture", label = 'Picture'),
      downloadButton('report', label = 'Download PDF')
      ),

    mainPanel()
  )
)

server <- function(input,output){

  picture <-  reactive({
    input$picture[,4]
  })

  output$report = downloadHandler(
    filename = "test.pdf",

    content = function(file){
      picture = picture()

    out = knit2pdf(input = 'test.Rnw', compiler = 'xelatex', clean = TRUE)
    file.rename(out, file) 
    },

    contentType = 'application/pdf'
  )
}

shinyApp(ui = ui, server = server)

and the .Rnw document:

\documentclass{article}

\begin{document}

Put picture here:
<<echo = FALSE , message = F, results='asis'>>=
cat(paste('\\includegraphics[height=3in]{', picture,'}'))
@

\end{document}

Part '\includegraphics[height=3in]{', picture,'} is obviously causing the problem, because I do not know the picture path only temporary one.

like image 491
An economist Avatar asked Jul 14 '16 10:07

An economist


2 Answers

You said you were working with Shiny Server, then you should be okay with the full path of the picture, even if it is in a temporary directory (because currently Shiny Server only works on Linux, and LaTeX should be okay with Linux file paths like /tmp/...../yourfile.png). The problem is perhaps the datapath (i.e. input$picture[, 4]) does not have the file extension, so LaTeX could not recognize it. You may try to retrieve the filename extension of the original file, and copy the uploaded picture to a temp file with the same extension, e.g.

picture <- reactive({
  path1 <- input$picture$datapath
  path2 <- tempfile(fileext = gsub('^(.*)([.].+)$', '\\2', input$picture$name))
  file.copy(path1, path2, overwrite = TRUE)
  path2
})
like image 143
Yihui Xie Avatar answered Oct 23 '22 20:10

Yihui Xie


i see a solution in two ways:

1) copy the temporary file to a folder of your choice and use that image:

observe({
        if (is.null(input$picture)) return()
        picture<-"your/final/path/to/disk/uploadImage.jpg" # OR do a PASTE with the PATH and the upload file name
        file.copy(input$picture$datapath, picture)
        if(file.exists(picture)){
          # PROCESS THE IMAGE IF NEEDED
        }
        picture<<-picture # sometimes needed to R to see the variable outside the observe scope
 })

2) if you (in this case the R session) are not allowed to write to disk you can turn the image into a base64 variable and include that into your Knitr document (or save it to a database as a string). This takes the Knitr/HTML route if you are willing to take that detour. (R studio running from a server almost always has a lot of restrictions in reading/writing that you can only handle as an ADMIN. And the server runs the shiny session as RStudio and not you so Rstudio must have the read/write permissions needed if you run the Shiny app as an automatic Rstudio Shiny session and not run it directly form RStudio using RUN)

Make sure the base64 is readable by R ouside the 'observe' or 'if' scope again by using '<<-' together with '<-'. Scoping is quite something special with R so be sure to test it properly.

You should dive into this (base64) with sites like:

https://github.com/yihui/knitr/issues/944

https://github.com/yihui/knitr/blob/master/R/utils-base64.R

like image 33
irJvV Avatar answered Oct 23 '22 20:10

irJvV