R Shiny:如何将含多行单元格的DataTable导出为PDF及Excel?
保留格式导出带多行单元格的Shiny表格(PDF/Excel)
问题说明
Shiny中的表格使用HTML标签(<br>换行、<b>加粗)在界面显示正常,但DT默认PDF导出、huxtable导出均无法保留这些格式;同时需要实现Excel导出时也保留单元格内换行与格式,且方案需适配shinyapps.io(不能依赖外部应用)。
PDF导出实现(保留换行与加粗)
核心思路是将HTML格式转换为Markdown格式,再通过rmarkdown渲染成PDF,无需外部工具,完全适配shinyapps.io。
完整代码
library(shiny) library(DT) library(rmarkdown) library(stringr) # 预处理数据:将HTML标签转成Markdown格式 data <- data.frame( Name = c("Mr A", "Mrs B"), Description = c( "This is line 1.<br>Line 2.", "This is another cell with line 1.<br>Line 2 has some <b>bold text</b>." ) ) # 替换HTML换行和加粗标签 data$Description <- str_replace_all(data$Description, "<br>", "\n") data$Description <- str_replace_all(data$Description, "<b>(.*?)</b>", "**\\1**") ui <- dashboardPage( skin = "black", dashboardHeader(disable = TRUE), dashboardSidebar(disable = TRUE), dashboardBody( DT::dataTableOutput("Table"), br(), downloadButton("export_pdf", "导出PDF(保留格式)") ) ) server <- function(input, output, session) { # 显示表格 output$Table <- DT::renderDataTable( data, extensions = 'Buttons', rownames = FALSE, options = list( paging = FALSE, searching = TRUE, fixedColumns = TRUE, autoWidth = TRUE, ordering = TRUE, dom = '<t>' ), escape = FALSE, class = "display" ) # PDF导出逻辑 output$export_pdf <- downloadHandler( filename = function() { "formatted_table.pdf" }, content = function(file) { # 临时Rmd文件内容 rmd_content <- " --- output: pdf_document --- ```{r echo=FALSE, results='asis'} knitr::kable(data, format = 'markdown')
" # 写入临时Rmd temp_rmd <- tempfile(fileext = ".Rmd") writeLines(rmd_content, temp_rmd) # 渲染成PDF render(temp_rmd, output_file = file) }
)
}
shinyApp(ui, server)
### 关键步骤 1. **格式转换**:把HTML的`<br>`替换为换行符`\n`,`<b>...</b>`替换为Markdown粗体语法`**...**`,确保Markdown能识别格式。 2. **Rmarkdown渲染**:通过临时Rmd文件调用`knitr::kable`生成带格式的表格,再渲染为PDF,全程无需外部依赖。 --- ## Excel导出实现(保留换行与加粗) Excel原生支持单元格内换行和字体加粗,使用`openxlsx`包可以直接控制单元格格式,完美适配需求。 ### 完整代码 ```r library(shiny) library(DT) library(openxlsx) library(stringr) data <- data.frame( Name = c("Mr A", "Mrs B"), Description = c( "This is line 1.<br>Line 2.", "This is another cell with line 1.<br>Line 2 has some <b>bold text</b>." ) ) ui <- dashboardPage( skin = "black", dashboardHeader(disable = TRUE), dashboardSidebar(disable = TRUE), dashboardBody( DT::dataTableOutput("Table"), br(), downloadButton("export_excel", "导出Excel(保留格式)") ) ) server <- function(input, output, session) { output$Table <- DT::renderDataTable( data, extensions = 'Buttons', rownames = FALSE, options = list( paging = FALSE, searching = TRUE, fixedColumns = TRUE, autoWidth = TRUE, ordering = TRUE, dom = '<t>' ), escape = FALSE, class = "display" ) # Excel导出逻辑 output$export_excel <- downloadHandler( filename = function() { "formatted_table.xlsx" }, content = function(file) { # 创建工作簿和工作表 wb <- createWorkbook() addWorksheet(wb, "Table") # 预处理数据:替换HTML换行 export_data <- data export_data$Description <- str_replace_all(export_data$Description, "<br>", "\n") # 写入基础数据 writeData(wb, "Table", export_data, startRow = 1, startCol = 1, rowNames = FALSE) # 设置单元格自动换行 wrap_style <- createStyle(wrapText = TRUE) addStyle(wb, "Table", wrap_style, rows = 2:(nrow(export_data)+1), cols = 2, gridExpand = TRUE) # 处理加粗格式:提取<b>标签内的文本并设置加粗 for (i in 1:nrow(export_data)) { desc <- export_data$Description[i] if (str_detect(desc, "<b>(.*?)</b>")) { bold_text <- str_extract(desc, "<b>(.*?)</b>") %>% str_remove_all("<b>|</b>") # 找到加粗文本在单元格中的位置 start_pos <- str_locate(desc, "<b>")[1,1] end_pos <- str_locate(desc, "</b>")[1,2] - 7 # 减去标签长度 # 创建加粗样式 bold_style <- createStyle(textDecoration = "bold") addStyle(wb, "Table", bold_style, rows = i+1, cols = 2, startChar = start_pos, endChar = end_pos, gridExpand = FALSE, stack = TRUE) # 移除HTML标签 export_data$Description[i] <- str_remove_all(desc, "<b>|</b>") # 更新单元格内容 writeData(wb, "Table", export_data$Description[i], startRow = i+1, startCol = 2, colNames = FALSE) } } # 保存工作簿 saveWorkbook(wb, file, overwrite = TRUE) } ) } shinyApp(ui, server)
关键步骤
- 换行设置:将HTML的
<br>替换为Excel识别的换行符\n,并设置单元格wrapText = TRUE实现自动换行。 - 加粗格式:提取
<b>标签内的文本,通过addStyle指定字符位置设置加粗样式,最后移除HTML标签更新单元格内容。
整合PDF+Excel导出的完整代码
可以将两个导出功能合并到同一个Shiny应用中:
library(shiny) library(DT) library(rmarkdown) library(openxlsx) library(stringr) data <- data.frame( Name = c("Mr A", "Mrs B"), Description = c( "This is line 1.<br>Line 2.", "This is another cell with line 1.<br>Line 2 has some <b>bold text</b>." ) ) # 预处理用于PDF的Markdown格式数据 pdf_data <- data pdf_data$Description <- str_replace_all(pdf_data$Description, "<br>", "\n") pdf_data$Description <- str_replace_all(pdf_data$Description, "<b>(.*?)</b>", "**\\1**") ui <- dashboardPage( skin = "black", dashboardHeader(disable = TRUE), dashboardSidebar(disable = TRUE), dashboardBody( DT::dataTableOutput("Table"), br(), downloadButton("export_pdf", "导出PDF(保留格式)"), br(), br(), downloadButton("export_excel", "导出Excel(保留格式)") ) ) server <- function(input, output, session) { output$Table <- DT::renderDataTable( data, extensions = 'Buttons', rownames = FALSE, options = list( paging = FALSE, searching = TRUE, fixedColumns = TRUE, autoWidth = TRUE, ordering = TRUE, dom = '<t>' ), escape = FALSE, class = "display" ) # PDF导出 output$export_pdf <- downloadHandler( filename = function() { "formatted_table.pdf" }, content = function(file) { rmd_content <- " --- output: pdf_document --- ```{r echo=FALSE, results='asis'} knitr::kable(pdf_data, format = 'markdown')
" temp_rmd <- tempfile(fileext = ".Rmd") writeLines(rmd_content, temp_rmd) render(temp_rmd, output_file = file) }
)
Excel导出
output$export_excel <- downloadHandler(
filename = function() { "formatted_table.xlsx" },
content = function(file) {
wb <- createWorkbook()
addWorksheet(wb, "Table")
export_data <- data export_data$Description <- str_replace_all(export_data$Description, "<br>", "\n") writeData(wb, "Table", export_data, startRow = 1, startCol = 1, rowNames = FALSE) wrap_style <- createStyle(wrapText = TRUE) addStyle(wb, "Table", wrap_style, rows = 2:(nrow(export_data)+1), cols = 2, gridExpand = TRUE) for (i in 1:nrow(export_data)) { desc <- export_data$Description[i] if (str_detect(desc, "<b>(.*?)</b>")) { bold_text <- str_extract(desc, "<b>(.*?)</b>") %>% str_remove_all("<b>|</b>") start_pos <- str_locate(desc, "<b>")[1,1] end_pos <- str_locate(desc, "</b>")[1,2] - 7 bold_style <- createStyle(textDecoration = "bold") addStyle(wb, "Table", bold_style, rows = i+1, cols = 2, startChar = start_pos, endChar = end_pos, gridExpand = FALSE, stack = TRUE) export_data$Description[i] <- str_remove_all(desc, "<b>|</b>") writeData(wb, "Table", export_data$Description[i], startRow = i+1, startCol = 2, colNames = FALSE) } } saveWorkbook(wb, file, overwrite = TRUE) }
)
}
shinyApp(ui, server)
内容的提问来源于stack exchange,提问作者PLA
相关产品推荐
相关产品推荐

