8

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.

An economist
  • 1,301
  • 1
  • 15
  • 35
  • 1
    uploaded file path is `input$picture$datapath` – Xiongbing Jin Jul 15 '16 at 16:59
  • Yes but it Is path to temporary directory and I have no idea how to make it work. Moreover, $datapath is the same as [,4] in my code. – An economist Jul 15 '16 at 17:03
  • Try replacing `paste` with `paste0` in your `.Rnw` file, otherwise there is a trailing space after the file name and the picture is not found. – NicE Jul 26 '16 at 14:37
  • I still get an error: `running 'texi2dvi' on 'test.tex' failed LaTeX errors: C:/Users/meme/Desktop/test.tex:55: LaTeX Error: File C:/ea9c33bfd57973b840657c82/0' not found.` – An economist Jul 27 '16 at 14:45

2 Answers2

2

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
})
Yihui Xie
  • 28,913
  • 23
  • 193
  • 419
  • Thank you very much for the answer but I still run i to the error `Running 'texi2dvi' on 'test.tex' failed. LaTeX errors: ! LaTeX Error: File '/tmp/RtmpoSS6qz/4683e6ec5bcf520337300eb1/0' not found.` I am probably missing something very basic but I really cannot figure it out. – An economist Jul 28 '16 at 09:08
  • Furthermore, I assigned the reactive function `picture()` to simple `picture` with the `content = function(file){picture = picture()` – An economist Jul 28 '16 at 09:17
  • I see. Sorry I missed that. Will update the answer in a minute. – Yihui Xie Jul 28 '16 at 15:55
  • No need to apologies I am really grateful for your interest and help. – An economist Jul 28 '16 at 15:59
  • It is a bit confusing to use the name `picture` everywhere, though. It is the input ID, the name of the reactive, and the name of the actual path in the `content` function. – Yihui Xie Jul 28 '16 at 16:04
  • Yes, I totally understand I did not think through naming in the working example. Nevertheless, your answer works perfectly fine, thank you very much for help! – An economist Jul 28 '16 at 16:07
1

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

irJvV
  • 892
  • 8
  • 26
  • Thank you very much for the response. I will work on the implementation and let you know if your approach allowed me to solve the problem. As you pointed out I am actually working with Shiny server, therefore there are issues with writing permissions. That is why I was using solution provided [here](http://stackoverflow.com/questions/35800883/using-image-in-r-markdown-report-downloaded-from-shiny-app) but for some predefined images which are placed on the server. I was wondering if it could be applied to this case when the image is uploaded by user? – An economist Jul 18 '16 at 12:38
  • @An economist, I would always work with complete system paths (depending on the system starting from the ROOT with C:/.... or /.../... or .. and not hope to find it in the same or nearby folder. Can work but some R programmers like to shift their working directory ( with setwd() and getwd() --->>> not me though =^) and then you are in the blind .... You can, by the way, always get the temp file directory with the function call: tempdir() – irJvV Jul 18 '16 at 13:37