You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R Shiny:从远程HPC服务器获取PDF并在iframe中展示的方法

解决方案:从HPC服务器直接在Shiny应用中展示PDF

针对你要从公司HPC服务器(数千个PDF,无法复制到Shiny的www/目录)展示PDF的需求,以下是几种可行方案及最佳实践:

方法1:通过SSHFS挂载HPC目录到Shiny服务器

将HPC的PDF目录挂载到Shiny服务器本地,让Shiny像访问本地文件一样读取PDF,是最直接的方案。

步骤:

  1. 在Shiny服务器上安装sshfs(例如Ubuntu上执行sudo apt install sshfs)。
  2. 用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)实现自动挂载。

  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 05:20:07