R Shiny:从远程HPC服务器获取PDF并在iframe中展示的方法
解决方案:从HPC服务器直接在Shiny应用中展示PDF
针对你要从公司HPC服务器(数千个PDF,无法复制到Shiny的www/目录)展示PDF的需求,以下是几种可行方案及最佳实践:
方法1:通过SSHFS挂载HPC目录到Shiny服务器
将HPC的PDF目录挂载到Shiny服务器本地,让Shiny像访问本地文件一样读取PDF,是最直接的方案。
步骤:
- 在Shiny服务器上安装
sshfs(例如Ubuntu上执行sudo apt install sshfs)。 - 用SSH密钥(凭证存环境变量)挂载HPC目录到Shiny服务器的某个路径,示例命令:
sshfs ${HPC_SSH_USER}@${HPC_SSH_HOST}:/hpc/pdf_dir /mnt/hpc-pdfs -o IdentityFile=/path/to/ssh-key -o allow_other
可以将该命令加入系统启动脚本(如/etc/fstab)实现自动挂载。
- 修改Shiny代码,用
addResourcePath将挂载目录注册为Shiny可访问的资源路径:
library(shiny) # 从环境变量读取HPC配置 HPC_MOUNT_DIR <- Sys.getenv("HPC_MOUNT_DIR") ui <- fluidPage( selectInput("select_pdf", "Select PDF file", choices = list.files(HPC_MOUNT_DIR, pattern = "\\.pdf$")), actionButton("btn_view_pdf", "View PDF"), htmlOutput("pdfviewer") ) server <- function(input, output) { # 注册挂载目录为Shiny资源路径 addResourcePath("hpc_pdfs", HPC_MOUNT_DIR) pdf_filepath <- eventReactive(input$btn_view_pdf, { file.path("hpc_pdfs", input$select_pdf) }) output$pdfviewer <- renderText({ req(pdf_filepath()) paste('<iframe style="height:1000px; width:100%" src="', pdf_filepath(), '"></iframe>', sep = "") }) } shinyApp(ui = ui, server = server)
方法2:动态下载PDF到临时目录再展示
如果无法挂载目录,可在用户选择PDF后,通过SSH下载到Shiny服务器的临时目录,展示后自动清理。
library(shiny) # 环境变量存储SSH凭证 SSH_USER <- Sys.getenv("HPC_SSH_USER") SSH_HOST <- Sys.getenv("HPC_SSH_HOST") HPC_PDF_DIR <- Sys.getenv("HPC_PDF_DIR") ui <- fluidPage( selectInput("select_pdf", "Select PDF file", choices = c()), actionButton("btn_view_pdf", "View PDF"), htmlOutput("pdfviewer") ) server <- function(input, output, session) { # 预加载HPC上的PDF列表 observe({ tryCatch({ pdf_list <- system2("ssh", args = c(paste0(SSH_USER, "@", SSH_HOST), paste0("ls ", HPC_PDF_DIR, "/*.pdf")), stdout = TRUE) pdf_list <- basename(pdf_list) updateSelectInput(session, "select_pdf", choices = pdf_list) }, error = function(e) { showNotification("无法连接到HPC服务器", type = "error") }) }) # 临时目录存储下载的PDF temp_dir <- tempdir() addResourcePath("temp_pdfs", temp_dir) pdf_filepath <- eventReactive(input$btn_view_pdf, { req(input$select_pdf) hpc_path <- file.path(HPC_PDF_DIR, input$select_pdf) local_path <- file.path(temp_dir, input$select_pdf) # 用scp下载文件 system2("scp", args = c(paste0(SSH_USER, "@", SSH_HOST, ":", hpc_path), local_path)) file.path("temp_pdfs", input$select_pdf) }) output$pdfviewer <- renderText({ req(pdf_filepath()) paste('<iframe style="height:1000px; width:100%" src="', pdf_filepath(), '"></iframe>', sep = "") }) # 会话结束时清理临时PDF session$onSessionEnded(function() { unlink(list.files(temp_dir, pattern = "\\.pdf$", full.names = TRUE)) }) } shinyApp(ui = ui, server = server)
方法3:流式传输PDF(无本地存储)
通过Shiny的registerDataObj直接从HPC读取PDF内容并流式传给客户端iframe,无需在Shiny服务器存储文件,适合大文件场景。
library(shiny) SSH_USER <- Sys.getenv("HPC_SSH_USER") SSH_HOST <- Sys.getenv("HPC_SSH_HOST") HPC_PDF_DIR <- Sys.getenv("HPC_PDF_DIR") ui <- fluidPage( selectInput("select_pdf", "Select PDF file", choices = c()), actionButton("btn_view_pdf", "View PDF"), htmlOutput("pdfviewer") ) server <- function(input, output, session) { # 加载HPC PDF列表 observe({ tryCatch({ pdf_list <- system2("ssh", args = c(paste0(SSH_USER, "@", SSH_HOST), paste0("ls ", HPC_PDF_DIR, "/*.pdf")), stdout = TRUE) pdf_list <- basename(pdf_list) updateSelectInput(session, "select_pdf", choices = pdf_list) }, error = function(e) { showNotification("HPC连接失败", type = "error") }) }) # 生成流式PDF的访问URL output$pdfviewer <- renderText({ req(input$btn_view_pdf, input$select_pdf) hpc_path <- file.path(HPC_PDF_DIR, input$select_pdf) # 注册数据对象,通过SSH读取PDF内容并返回 pdf_url <- session$registerDataObj( id = paste0("pdf_", input$select_pdf), data = NULL, filter = function(data, req) { tmp_file <- tempfile(fileext = ".pdf") system2("ssh", args = c(paste0(SSH_USER, "@", SSH_HOST), paste0("cat ", hpc_path)), stdout = tmp_file) shiny:::httpResponse( 200, content_type = "application/pdf", content = readBin(tmp_file, "raw", file.info(tmp_file)$size) ) } ) paste('<iframe style="height:1000px; width:100%" src="', pdf_url, '"></iframe>', sep = "") }) } shinyApp(ui = ui, server = server)
最佳实践
- 安全:所有SSH凭证(用户名、主机、密钥路径)必须存在环境变量中,禁止硬编码;SSH密钥文件设置
chmod 600权限;限制HPC端Shiny用户的只读权限。 - 性能:挂载目录(方法1)适合高频访问的PDF;流式传输(方法3)适合大文件,节省磁盘空间;若PDF数量过千,需给下拉框添加搜索过滤(用
selectizeInput替代selectInput)避免UI卡顿。 - 稳定性:挂载目录时,在
fstab中添加_netdev,x-systemd.automount实现断连自动重连;所有SSH操作添加tryCatch捕获错误并提示用户。
内容的提问来源于stack exchange,提问作者mandmeier
相关产品推荐
相关产品推荐

