14
  • Example
  • Problem statement
  • Solution search and
  • Question

... see rmarkdown example code.

Appreciate answers demonstrating the solution by modifying the rmarkdown snippet.

---
title: "Reproducable Example"
author: "user2030503"
output: html_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

## Example: mtcars

```{r}
write.csv2(mtcars, "./file.csv")

# Code to embed mtcars as csv
# Code to provide mechanism for button or link for later user interaction to open/save the csv. 
```

## Problem

* I want to embed the csv file into the html generated by this rmarkdown script.
* Embedding here means, that the csv data are integral part of the hmtl (i.e. for offline use).
* I want a mechanism (button or link) in the html, which allows the user to open/save the data the csv.

## Search for a solution

There are techniques for embedding rdata files.

* http://rmarkdown.rstudio.com/articles_rdata.html
* https://github.com/richarddmorey/BayesFactorExtras/blob/master/BayesFactorExtras/R/downloadURI.R

## Question

* Dispite of above approaches, I did not find a solution yet how to solve the problem.
* How can it be achieved demonstrating it via this reproducable example ?
user2030503
  • 3,064
  • 2
  • 36
  • 53

3 Answers3

25

No javascript; no widgets; no extra CSS; 4 LoC (cld be 1 LoC if you like unreadable code):

```{r}
write.csv2(mtcars, "./file.csv")

library(magrittr)
readLines("./file.csv") %>% 
  paste0(collapse="\n") %>% 
  openssl::base64_encode() -> encoded
```

[Download CSV](`r sprintf('data:text/csv;base64,%s', encoded)`)

Fairly straightforward:

  • treat the file as just "stuff" and read it in as lines
  • make it all one blob of newline separated text
  • encode it into base 64
  • make a data URI with the proper media type
  • embed it as a markdown link

You can also do something like:

<a download="mtcars.csv" href="`r sprintf('data:text/csv;base64,%s', encoded)`">Straight HTML Download Link</a>

if you want to give browsers (and, hence, users) a suggested filename (HTML placement in markdown rules apply).

NOTE:

readBin("./file.csv", "raw", file.info("./file.csv")$size) %>% 
  openssl::base64_encode() -> encoded

also works equally as well as the readLines() version.

hrbrmstr
  • 77,368
  • 11
  • 139
  • 205
  • Unfortunately this solution does not work in IE 11 (Windows 8). The link generated by the solution does not Trigger anything. How can it be fixed ? – user2030503 Dec 19 '16 at 13:31
  • 2
    Aye, use a real browser ;-) It seems that this is a known problem with IE 11 being brain dead: http://caniuse.com/#feat=datauri ; that means you can't embed this type of data URI for IE 11. It looks like Edge is also brain dead, too. – hrbrmstr Dec 19 '16 at 13:45
  • This solution is great for Chrome but in Safari, a download is not initiated and we just see the csv displayed on the browser. Is there a workaround? – iskandarblue Feb 18 '18 at 06:44
  • You can try `application/vnd.ms-excel` instead of `text/csv` and it should consistently download on all "supported" platforms. – hrbrmstr Feb 18 '18 at 15:26
  • That may help, but produces warnings in Chrome + Excel that the downloaded file is not actual XLS if you use the markdown approach to embed. – Surpdeh Nov 20 '18 at 23:50
  • Nice complaint. Solution? – hrbrmstr Nov 21 '18 at 01:33
  • Any idea how a download link like this could be included in a kable caption? – Brendan Mar 11 '22 at 10:52
4

How about something like this:

---
title: "Reproducable Example"
author: "dimitris_ps "
date: "17 December 2016"
output: html_document
---

<style>
  #DataTables_Table_0 {
     visibility: hidden;
  }

  #DataTables_Table_0_paginate {
    visibility: hidden;
  }

</style>

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(DT)

dt <-   datatable(mtcars, rownames=T, 
            # filter = 'top',
              callback=JS('$("a.buttons-collection").css("background","#008CBA");
           $("a.buttons-collection").css("font-size","15px");
           $("a.buttons-collection").css("border-radius", "8px");
           $("a.buttons-collection").css("margin-right","0px");
           return table;'),
        extensions = 'Buttons',
        options = list(searching=F,
                       paging = T,
                       bInfo = F,
                       columnDefs = list(list(className = 'dt-left',  targets = 0),
                                         list(className = 'dt-center',  targets = 1:11)),
                       pageLength = 1,
                       initComplete = JS("function(settings, json) {",
                                         "$(this.api().table().header()).css({'background-color': '#99ccff', 'color': '#003333'});",
                                         "}"),
                       dom = 'Bfrtip',
                       buttons = list(
                                      list(extend = 'collection',
                                           buttons = c('excel', 'csv'),
                                           text = 'DOWNLOAD DATA')
                       )
        )
  )

```
<br>

```{r mtcars, echo=FALSE}
dt
```

You will need to have the DT library installed

dimitris_ps
  • 5,849
  • 3
  • 29
  • 55
  • Appreciate your effort. Two issues: 1. is a small one: The two JS functions require a DT:: prefix. 2. is a bigger one: I do not want to display a table. Can you refactor so that only the buttons remain ? – user2030503 Dec 17 '16 at 11:27
  • On you point 1, load the `library(DT)`, this is what i had initially. Thanks for pointing it out. On your point2 there might be a workaround with `css` hide table. I will come back to this later – dimitris_ps Dec 17 '16 at 11:33
  • I have updated my answer. Not the best approach, but is close to what you are looking for. Basically, i am hiding the `DataTable` with `css` – dimitris_ps Dec 17 '16 at 11:47
  • 1
    Nice, at the end what I was looking for, thx for your kind help. As the overhead of your solution is higher, I picked the answer of user hrbrmstr. Hope you understand. The advantage of your solution is that it offers also Excel export. – user2030503 Dec 17 '16 at 15:16
  • Sure no prob, it makes sense, its much more straightfoward – dimitris_ps Dec 18 '16 at 00:52
  • Could you have the table hidden, but with a button to open the table for viewing? – Mark Neal Nov 19 '19 at 03:40
  • Sort of like this [link](https://gist.github.com/bborgesr/c628236dabb639d57eab3e16da69920f) but for R/rmarkdown instead of shiny – Mark Neal Nov 19 '19 at 03:47
4

Based on the answer of user hrbrmstr I prepared a convenience function embed_data(), see it in action:

---
title: "Untitled"
author: "user2030503"
date: "17 12 2016"
output: html_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
``` 

```{r echo=FALSE}

embed_data= function(x= mtcars, filename= "file.csv", label= "Get data"){

  # Create encoded Base64 datastream 
  encode_data= function(x){
    write.csv2(x, "./file.csv")
    enc= sprintf('data:text/csv;base64,%s', openssl::base64_encode(paste0(readLines("./file.csv"), collapse="\n")) )
    unlink("./file.csv")
    return(enc)
  }

  # String result ready to be placed in rmarkdown
  paste0("<a download='", filename, "' href=", encode_data(x), ">", label, "</a>")

}
```

`r embed_data(mtcars, filename="mtcars.csv")` 
Community
  • 1
  • 1
user2030503
  • 3,064
  • 2
  • 36
  • 53
  • If i save to the same filename a number of times, but with new data it doesn't completely delete the old data from the file. So if the new data has 5 rows, and old data 10 rows, the bottom 5 rows of the old data remain. Is there a way to replace the whole file? – dule arnaux Sep 21 '18 at 15:42