记录通过vue-pdf实现打印文件预览功能遇到问题:跨域、https时不能使用http获取pdf、证书认证不通过

一、环境说明

前端 vue + 后端 springboot + 文件服务器nginx
项目:https端口8086、http端口8084
文件服务器:http端口35105
注:前后端未分离 ,都在Tomcat容器中

二、问题说明

1.跨域
项目端口为8086或8084,而文件服务器端口为35105,因此直接通过前端发送请求时存在跨域问题。
2.https时不能使用http获取pdf
当使用https协议打开服务端时,因文件服务器对应URL为http协议,浏览器会出于安全考虑对请求进行自动拦截。
3.文件服务器证书为自定义证书,不能通过浏览器的SSL认证。

三、解决方法

方式一:后端接口调用

说明:前端通过调用后端接口获取文件服务器中对应文件的流,从而在前端展示。

前端部分代码示例:

	// 预览pdf文件
    handlePreviewPdfFile () {
      const newFormData = new FormData()
      newFormData.append('url', this.url)
      apiAPI.previewPdfFile(newFormData).then(res => {
        this.pdfUrl = this.getObjectURL(res.data)
      })
    },
    // 将返回的流数据转换为url
    getObjectURL (file) {
      let url = null
      if (window.createObjectURL !== undefined) { // basic
        url = window.createObjectURL(file)
      } else if (window.webkitURL !== undefined) { // webkit or chrome
        try {
          url = window.webkitURL.createObjectURL(file)
        } catch (error) {
          console.log(error)
        }
      } else if (window.URL !== undefined) { // mozilla(firefox)
        try {
          url = window.URL.createObjectURL(file)
        } catch (error) {
          console.log(error)
        }
      }
      return url
    }

后端部分代码示例:

public void previewPdfFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String url = request.getParameter("url");
        if (StringUtils.isBlank(url)) {
            log.error("请求参数url为空");
            return;
        }
        FileDownLoadConfig fileDownLoadConfig = new FileDownLoadConfig();
        //基准参照路径
        String baseDir = fileDownLoadConfig.getFileDownloadPath();
        // 文件下载时服务器的临时文件夹,用于存放多个文件
        String dir = baseDir + File.separator + UUID.randomUUID().toString().replaceAll("\\-", "");
        //删除重名的临时文件夹
        File tempFile = new File(dir);
        if (tempFile.exists() && tempFile.isDirectory()) {
            FileOperateUtil.delFilesApache(dir);
        }
        tempFile.mkdirs();
        //将文件服务器文件下载到dir
        DownloadFileFromURLUtil.downloadFromUrl(url, dir);
        // 获取下载下来的文件
        String fileName = DownloadFileFromURLUtil.getFileNameFromUrl(url);
        if (IPUtil.isWindowsOS()) {
            fileName = "1.zip";
        }
        File file = new File(dir + File.separator + fileName);
        // 设置response
        response.reset();
        response.setContentType(MediaType.APPLICATION_PDF_VALUE);
        response.setHeader("Access-Control-Expose-Headers", "filename");
        response.addHeader("filename", java.net.URLEncoder.encode(fileName, "UTF8"));
        // 返回
        BufferedInputStream in = null;
        OutputStream out = null;
        try {
            in = new BufferedInputStream(Files.newInputStream(file.toPath()));
            out = response.getOutputStream();
            int bytes = 0;
            byte[] bufferOut = new byte[1024];
            while ((bytes = in.read(bufferOut)) != -1) {
                out.write(bufferOut, 0, bytes);
            }
            out.flush();
        } catch (Exception e) {
            log.error("预览pdf文件失败:{} ===> 详情:{}", e.getMessage(), e);
            throw new BizCommonException("预览pdf文件失败:返回异常");
        } finally {
            if (null != out) {
                out.close();
            }
            if (null != in) {
                in.close();
            }
            //删除临时文件目录
            FileOperateUtil.delFilesApache(dir);
        }
    }

方式二:另起标签页

说明:在前端增加一个白名单页面preview-pdf.vue(类似login页面),当点击预览按钮时浏览器另外打开一个标签页http://XXX:8084/XX/preview-pdf。
注:默认情况下,我们项目中http协议未开启,故前后端需要对http协议下的preview-pdf页面增加白名单

点击预览按钮方法部分代码示例:

	async handlePreviewPdf () {
      let httpPort = ''
      await api.getPort().then(res => {
        // 心跳http端口
        httpPort = res
      })
      if (httpPort === '') {
        return
      }
      Cookies.set('previewPdfUrl', this.previewPdfUrl)
      Cookies.set('previewPdfPassword', this.previewPdfPassword)
      window.open('http://' + window.location.hostname + ':' + httpPort + '/XX/preview-pdf')
    }

preview-pdf页面部分代码示例:

<template>
  <div class="pdf-preview-container">
    <div class="header">
        <h2>pdf文件预览</h2>
    </div>
    <div class="pdf-container">
        <VuePdf :url="pdfData.previewPdfUrl" :password="pdfData.previewPdfPassword"/>
    </div>
  </div>
</template>
<script>
import Cookies from 'js-cookie'
import VuePdf from '@/components/VuePdf/index.vue'

export default {
  // 组件名称
  name: 'PreviewPdf',

  // 组件
  components: { VuePdf },

  // 数据
  data () {
    return {
      // pdf预览数据
      pdfData: {
        // pdf文件路径
        previewPdfUrl: '',
        // pdf文件密码
        previewPdfPassword: ''
      }
    }
  },

  // 生命周期 created
  created () {
    this.handlePreview()
  },

  mounted () {
  // 右键事件监听器
    this.contextMenuListener = function (e) {
      e.preventDefault()
      e.stopPropagation()
    }
    document.addEventListener('contextmenu', this.contextMenuListener)

    // 键盘事件监听器
    this.keyDownListener = function (e) {
      e.preventDefault()
      e.stopPropagation()
    }
    document.addEventListener('keydown', this.keyDownListener)
  },

  destroyed () {
    // 移除右键事件监听器
    document.removeEventListener('contextmenu', this.contextMenuListener)
    // 移除键盘事件监听器
    document.removeEventListener('keydown', this.keyDownListener)
  },

  // 方法
  methods: {
    handlePreview () {
    // 取cookie
      const key = ['previewPdfUrl', 'previewPdfPassword']
      this.pdfData = {
        previewPdfUrl: Cookies.get('previewPdfUrl'),
        previewPdfPassword: Cookies.get('previewPdfPassword')
      }
      key.forEach(item => {
        Cookies.remove(item)
      })
    }
  }
}
</script>
<style lang="less" scoped>
.pdf-preview-container {
  display: flex;
  flex-direction: column;
  height: 100vh; /* 设置为视口高度,这样容器会占据整个页面 */
  overflow: hidden; /* 确保外部容器不显示滚动条 */
}

.header {
  text-align: center; /* 使h2标签内容水平居中 */
  padding: 10px; /* 可根据需要添加内边距 */
}

.pdf-container {
  flex: 1; /* 使pdf容器占据剩余空间 */
  height: 90%; /* 设置高度为父容器(去除 header 后)的 90% */
  overflow: hidden; /* 确保外部容器不显示滚动条 */
}
</style>

文件服务器nginx支持跨域部分配置示例:

add_header Access-Control-Allow-Origin *;

方式三:网关拦截

说明:在前端和文件服务器中间增加一层网关。前端发出https协议请求,网关拦截后转换为http
网关部分配置示例:

spring:
  application:
    name: gateway-application
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOriginPatterns: "*"
            allowCredentials: true
            allowedMethods:
              - GET
              - POST
              - DELETE
              - PUT
              - PATCH
              - OPTIONS
            allowedHeaders: "*"
      default-filters:
        - DedupeResponseHeader=Vary Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_FIRST
      routes:
        # 与基础平台与子系统交互 后端 》》》》》》start
        - id: vios-preview
          uri: http://127.0.0.1:35105
          order: 1
          predicates:
            - Path=/XXXX/**

文件服务器nginx支持跨域部分配置示例:

add_header Access-Control-Allow-Origin *;
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值