前段时间公司需要做一个PDF预览功能记录一下遇到的问题以及实现的过程

  1. PDF.js 预览远程文件跨域问题
  2. SpringBoot文件流消息头类型选择
  3. 远程文件资源地址 不属于自己的管辖的范围,无法通过修改服务端请求头来解决跨域问题

话不多说开始了

1.PDF.js 下载
下载地址 http://mozilla.github.io/pdf.js/getting_started/#download
2.拷贝PDF.js 文件里的 build 、web 文件至 vue项目里的静态文件夹statics
在这里插入图片描述
3.PDF.js在vue项目中的使用
这里使用iframe的形式展示pdf,因为需要跳转到新的链接才能预览
下面pdf地址的传参方式根据自身可以调整,笔者因为zuul网关做了处理才这么传参

<template>
   <iframe class="prism-player" :src="url" width="100%" style=" height: 100vh;"></iframe>
</template>

<script>
import qs from 'qs'
export default {
    data () {
        return {
            url: ''
            
        }
    },
   
    created () {
        let dataJson = {
            pdfUrl : this.$route.query.url
        }
        let data = {
            'encData': JSON.stringify(dataJson)
        }
        
        // 这里可以换成你的pdf文件地址
        let url = this.$axiosHttp.axiosGetBaseURL() + "/org-v2-feedback/getFile/pdfFile?"+qs.stringify(data) 
		// pdf文件地址需要通过 encodeURIComponent 方法转译 因为是远程文件地址,/ 会被误解成地址一部分
		// 下面这个这个viewer.html地址根据自身位置修改
        this.url = './statics/pdf/web/viewer.html?file=' + encodeURIComponent(url)

    }

}
</script>

下面划重点了

4.SpringBoot 文件流 实现
作者采用Java爬虫(HttpClient)的方式获取远程文件,然后以文件流的形式返回
下面是笔者自己封装的Utils


import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;

public class HttpUtils {

    /**
     * 通过爬虫获取文件流
     * @param urlPath
     * @return
     */
    public static InputStream getYCFile(String urlPath) {
        InputStream inputStream = null;
        try {
            String strUrl = urlPath.trim();
            URL url = new URL(strUrl);
            // 如果不需要忽略ssl证书可以不加这部分
            if("https".equalsIgnoreCase(url.getProtocol())){ // 忽略https  ssl证书
                SslUtils.ignoreSsl();
            }
            //打开请求连接
            URLConnection connection = url.openConnection();
            HttpURLConnection httpURLConnection=(HttpURLConnection) connection;
            httpURLConnection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            // 取得输入流,并使用Reader读取
            inputStream = httpURLConnection.getInputStream();
            return inputStream;
        } catch (IOException e) {
            System.out.println(e.getMessage());
            inputStream = null;
        }  catch (Exception e) {
            System.out.println(e.getMessage());
            inputStream = null;
        }
        return inputStream;
    }

}

因为考虑到Https远程资源问题 需要忽略SSL证书


import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class SslUtils {
    private static void trustAllHttpsCertificates() throws Exception {
        TrustManager[] trustAllCerts = new TrustManager[1];
        TrustManager tm = new miTM();
        trustAllCerts[0] = tm;
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, null);
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }

    static class miTM implements TrustManager,X509TrustManager {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public boolean isServerTrusted(X509Certificate[] certs) {
            return true;
        }

        public boolean isClientTrusted(X509Certificate[] certs) {
            return true;
        }

        public void checkServerTrusted(X509Certificate[] certs, String authType)
                throws CertificateException {
            return;
        }

        public void checkClientTrusted(X509Certificate[] certs, String authType)
                throws CertificateException {
            return;
        }
    }

    /**
     * 忽略HTTPS请求的SSL证书,必须在openConnection之前调用
     * @throws Exception
     */
    public static void ignoreSsl() throws Exception{
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String urlHostName, SSLSession session) {
                System.out.println("Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost());
                return true;
            }
        };
        trustAllHttpsCertificates();
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    }

}

springBoot 接口返回数据流就可以了
使用文件地址作为参数传入


@Controller
@RequestMapping("/getFile")
public class GetFileController {

    /**
     * (HttpClient)爬虫获取各种资源pdf文件 跳ssl验证
     * @param response
     * @param dataJson pdf 文件路径
     */
    @GetMapping("/pdfFile")
    public void getFile(HttpServletResponse response, String dataJson) {
        if(StringUtils.isEmpty(dataJson)) {
            return;
        }
         // 这里可根据自身的需要返回的文件类型调整消息投类型  灵活一点也可做为参数
         response.setContentType("application/pdf");
         JSONObject object = JSONObject.parseObject(dataJson);
         String pdfUrl = object.getString("pdfUrl");
         InputStream inputStream = null;
         BufferedInputStream bins = null;
         OutputStream outs = null;
         BufferedOutputStream bouts = null;
         try {
             inputStream = HttpUtils.getYCFile(pdfUrl); // 使用爬虫获取文件流
             if(inputStream == null) {
                 return;
             }
             bins = new BufferedInputStream(inputStream);//放到缓冲流里面
             outs = response.getOutputStream(); // 获取输出流
             bouts = new BufferedOutputStream(outs); //放到缓冲流里面

             int bytesRead = 0;
             byte[] buffer = new byte[1024];
             //开始向网络传输文件流
             while ((bytesRead = bins.read(buffer, 0, 1024)) != -1) {
                 bouts.write(buffer, 0, bytesRead);
             }
             bouts.flush();//这里一定要调用flush()方法

         } catch (IOException e) {
             e.printStackTrace();
         }finally {
             try {
                 if (inputStream != null) {
                     inputStream.close();
                 }
                 if (bins != null) {
                     bins.close();
                 }
                 if (outs != null) {
                     outs.close();
                 }
                 if (bouts != null) {
                     bouts.close();
                 }
             } catch (IOException e) {
                 e.printStackTrace();
             }

         }
    }

}

到这里就结束了,上面pdf地址调用接口地址就可以啦

注意:vue地址必须和springboot 地址 域名必须一致 不然又要跨域啦

如果没有办法一致 SpringBoot 服务端 处理下请求头 就可以了 至于怎么处理 百度一下

Logo

前往低代码交流专区

更多推荐