Java实现PDF在线预览

Java实现PDF在线预览

前言:之前一直PDF一直是下载后再查看,一直在想如何如何在线预览,现已找到方法,作此笔记,也希望都对其他人有所帮助
之前的pdf预览新增了文件路径的加密

后面加入了word,excel,ppt的预览,不过需要下载: openoffice,具体教程可以自己搜索

代码实现

@Slf4j
@Controller
@RequestMapping("/file/view")
public class FileViewController {


     /**
     * 预览pdf
     * @param request
     * @param response
     */
    @GetMapping("/pdf")
    public void preViewPDF(HttpServletRequest request, HttpServletResponse response) {
        String fileName = request.getParameter("fileName");
        int dotIndex = fileName.lastIndexOf(".");
        String encryptPdfFilePath = fileName.substring(0, dotIndex).replaceAll(" ", "+");
        String pdfFilePath = AESUtil.decryptCBC(encryptPdfFilePath, SystemUtils.FILE_AES_PRIVATE_KEY);
        String suffix = FilenameUtils.getExtension(fileName); // 获取文件后缀
        pdfFilePath = pdfFilePath + "." + suffix;
        String replace = pdfFilePath.replace('_', File.separatorChar);
        try (
                FileInputStream inputStream = new FileInputStream(replace);
                OutputStream outputStream = response.getOutputStream()
        ) {
            MimeTypeEnum mimeTypeEnum = MimeTypeEnum.findByExtension(suffix);
            if (mimeTypeEnum == null || StringUtils.equalsAny(mimeTypeEnum.name(),
                    MimeTypeEnum.PDF.name(),
                    MimeTypeEnum.PNG.name(),
                    MimeTypeEnum.JPG.name(),
                    MimeTypeEnum.JPEG.name())) {
                throw new FileException(101, "该文件类型不支持预览");
            }
            response.setContentType(mimeTypeEnum.getMimeType());
            IOUtils.write(IOUtils.toByteArray(inputStream), outputStream);
        } catch (FileNotFoundException e) {
            log.error("pdf路径不存在", e);
        } catch (IOException e) {
            log.error("写入出现IO异常", e);
        }
    }

 @GetMapping("/pdf_base64")
    @ResponseBody
    public String htmlViewPDF(String pdfFilePath){
        String base64 = "";
        String replace = pdfFilePath.replace('_', File.separatorChar);
        try (FileInputStream inputStream = new FileInputStream(replace)){
            byte[] data = IOUtils.toByteArray(inputStream);
            base64 = Base64Utils.encodeToString(data);
            base64 = base64.replaceAll("\r\n",""); // 去除空格和换行替换
            base64 = base64.replaceAll("\\+","%2B"); // 对base64中的”+“进行转义
            base64 = "data:application/pdf;base64," + base64;
        } catch (FileNotFoundException e) {
            log.error("pdf路径不存在", e);
        } catch (IOException e) {
            log.error("写入出现IO异常", e);
        }
        return base64;
    }
    
	/**
     * 预览office文件
     */
    @GetMapping("/office")
    public void onlinePreview(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String fileName = request.getParameter("fileName");
        int dotIndex = fileName.lastIndexOf(".");
        String encryptPdfFilePath = fileName.substring(0, dotIndex).replaceAll(" ", "+");
        String pdfFilePath = AESUtil.decryptCBC(encryptPdfFilePath, SystemUtils.FILE_AES_PRIVATE_KEY);
        String suffix = FilenameUtils.getExtension(fileName); // 获取文件后缀
        pdfFilePath = pdfFilePath + "." + suffix;
        String replace = pdfFilePath.replace('_', File.separatorChar);
        try (
                InputStream in = FileConvertUtil.convertLocaleFile(replace, suffix);
                OutputStream outputStream = response.getOutputStream();
        ) {
            if (!StringUtils.equalsAnyIgnoreCase(suffix,
                    MimeTypeEnum.TXT.getExtension(),
                    MimeTypeEnum.DOC.getExtension(),
                    MimeTypeEnum.XLS.getExtension(),
                    MimeTypeEnum.PPT.getExtension())) {
                throw new FileException(101, "该文件类型不支持预览");
            }
            byte[] buff = new byte[1024]; //创建存放文件内容的数组
            int n;  //所读取的内容使用n来接收
            while ((n = in.read(buff)) != -1) { //当没有读取完时,继续读取,循环
                outputStream.write(buff, 0, n); //将字节数组的数据全部写入到输出流中
            }
            outputStream.flush(); //强制将缓存区的数据进行输出
        } catch (Exception e) {
            log.error("预览文件失败:", e);
        }
    }
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum MimeTypeEnum {

    AAC("acc","AAC音频","audio/aac"),

    ABW("abw","AbiWord文件","application/x-abiword"),

    ARC("arc","存档文件","application/x-freearc"),

    AVI("avi","音频视频交错格式","video/x-msvideo"),

    AZW("azw","亚马逊Kindle电子书格式","application/vnd.amazon.ebook"),

    BIN("bin","任何类型的二进制数据","application/octet-stream"),

    BMP("bmp","Windows OS / 2位图图形","image/bmp"),

    BZ("bz","BZip存档","application/x-bzip"),

    BZ2("bz2","BZip2存档","application/x-bzip2"),

    CSH("csh","C-Shell脚本","application/x-csh"),

    CSS("css","级联样式表(CSS)","text/css"),

    CSV("csv","逗号分隔值(CSV)","text/csv"),

    DOC("doc","微软Word文件","application/msword"),

    DOCX("docx","Microsoft Word(OpenXML)","application/vnd.openxmlformats-officedocument.wordprocessingml.document"),

    EOT("eot","MS Embedded OpenType字体","application/vnd.ms-fontobject"),

    EPUB("epub","电子出版物(EPUB)","application/epub+zip"),

    GZ("gz","GZip压缩档案","application/gzip"),

    GIF("gif","图形交换格式(GIF)","image/gif"),

    HTM("htm","超文本标记语言(HTML)","text/html"),

    HTML("html","超文本标记语言(HTML)","text/html"),

    ICO("ico","图标格式","image/vnd.microsoft.icon"),

    ICS("ics","iCalendar格式","text/calendar"),

    JAR("jar","Java存档","application/java-archive"),

    JPEG("jpeg","JPEG图像","image/jpeg"),

    JPG("jpg","JPEG图像","image/jpeg"),

    JS("js","JavaScript","text/javascript"),

    JSON("json","JSON格式","application/json"),

    JSONLD("jsonld","JSON-LD格式","application/ld+json"),

    MID("mid","乐器数字接口(MIDI)","audio/midi"),

    MIDI("midi","乐器数字接口(MIDI)","audio/midi"),

    MJS("mjs","JavaScript模块","text/javascript"),

    MP3("mp3","MP3音频","audio/mpeg"),

    MPEG("mpeg","MPEG视频","video/mpeg"),

    MPKG("mpkg","苹果安装程序包","application/vnd.apple.installer+xml"),

    ODP("odp","OpenDocument演示文稿文档","application/vnd.oasis.opendocument.presentation"),

    ODS("ods","OpenDocument电子表格文档","application/vnd.oasis.opendocument.spreadsheet"),

    ODT("odt","OpenDocument文字文件","application/vnd.oasis.opendocument.text"),

    OGA("oga","OGG音讯","audio/ogg"),

    OGV("ogv","OGG视频","video/ogg"),

    OGX("ogx","OGG","application/ogg"),

    OPUS("opus","OPUS音频","audio/opus"),

    OTF("otf","otf字体","font/otf"),

    PNG("png","便携式网络图形","image/png"),

    PDF("pdf","Adobe 可移植文档格式(PDF)","application/pdf"),

    PHP("php","php","application/x-httpd-php"),

    PPT("ppt","Microsoft PowerPoint","application/vnd.ms-powerpoint"),

    PPTX("pptx","Microsoft PowerPoint(OpenXML)","application/vnd.openxmlformats-officedocument.presentationml.presentation"),

    RAR("rar","RAR档案","application/vnd.rar"),

    RTF("rtf","富文本格式","application/rtf"),

    SH("sh","Bourne Shell脚本","application/x-sh"),

    SVG("svg","可缩放矢量图形(SVG)","image/svg+xml"),

    SWF("swf","小型Web格式(SWF)或Adobe Flash文档","application/x-shockwave-flash"),

    TAR("tar","磁带存档(TAR)","application/x-tar"),

    TIF("tif","标记图像文件格式(TIFF)","image/tiff"),

    TIFF("tiff","标记图像文件格式(TIFF)","image/tiff"),

    TS("ts","MPEG传输流","video/mp2t"),

    TTF("ttf","ttf字体","font/ttf"),

    TXT("txt","文本(通常为ASCII或ISO 8859- n","text/plain"),

    VSD("vsd","微软Visio","application/vnd.visio"),

    WAV("wav","波形音频格式","audio/wav"),

    WEBA("weba","WEBM音频","audio/webm"),

    WEBM("webm","WEBM视频","video/webm"),

    WEBP("webp","WEBP图像","image/webp"),

    WOFF("woff","Web开放字体格式(WOFF)","font/woff"),

    WOFF2("woff2","Web开放字体格式(WOFF)","font/woff2"),

    XHTML("xhtml","XHTML","application/xhtml+xml"),

    XLS("xls","微软Excel","application/vnd.ms-excel"),

    XLSX("xlsx","微软Excel(OpenXML)","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),

    XML("xml","XML","application/xml"),

    XUL("xul","XUL","application/vnd.mozilla.xul+xml"),

    ZIP("zip","ZIP","application/zip"),

    MIME_3GP("3gp", "3GPP audio/video container", "video/3gpp"),

    MIME_3GP_WITHOUT_VIDEO("3gp", "3GPP audio/video container doesn't contain video", "audio/3gpp2"),

    MIME_3G2("3g2", "3GPP2 audio/video container", "video/3gpp2"),

    MIME_3G2_WITHOUT_VIDEO("3g2", "3GPP2 audio/video container  doesn't contain video", "audio/3gpp2"),

    MIME_7Z("7z","7-zip存档","application/x-7z-compressed")
    ;

    private String extension;

    private String explain;

    private String mimeType;



    public static MimeTypeEnum findByExtension(String extension) {
        if(StringUtils.isBlank(extension)){
            return null;
        }
        for (MimeTypeEnum typesEnum : MimeTypeEnum.values()) {
            if (extension.equals(typesEnum.getExtension())) {
                return typesEnum;
            }
        }
        return null;
    }

    public static String getContentType(String fileType) {
        MimeTypeEnum mimeTypeEnum = MimeTypeEnum.findByExtension(fileType);
        if(mimeTypeEnum != null){
            return mimeTypeEnum.getMimeType();
        }
        return "application/octet-stream";
    }

}

AES加密算法代码

@Slf4j
public class AESUtil {
    /**
     * 编码
     */
    private static final String ENCODING = "UTF-8";
    /**
     * 算法定义
     */
    private static final String AES_ALGORITHM = "AES";
    /**
     * 指定填充方式
     */
    private static final String CIPHER_PADDING = "AES/ECB/PKCS5Padding";
    private static final String CIPHER_CBC_PADDING = "AES/CBC/PKCS5Padding";


    /**
     * AES加密
     *
     * @param content 待加密内容
     * @param aesKey  密码
     * @return
     */
    public static String encrypt(String content, String aesKey) {
        if (StringUtils.isBlank(content)) {
            log.info("AES加密内容不能为空");
            return null;
        }
        if (StringUtils.isNotBlank(aesKey) && aesKey.length() == 16) { //判断秘钥是否为16位
            try {
                byte[] bytes = aesKey.getBytes(ENCODING);  //对密码进行编码
                SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);  //设置加密算法,生成秘钥
                Cipher cipher = Cipher.getInstance(CIPHER_PADDING); // "算法/模式/补码方式"
                cipher.init(Cipher.ENCRYPT_MODE, skeySpec); //选择加密
                byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));  //根据待加密内容生成字节数组
                return Base64Utils.encodeToString(encrypted); //返回base64字符串
            } catch (Exception e) {
                log.info("AES加密失败:", e);
                throw new RuntimeException(e.getMessage());
            }
        } else {
            log.info("AES加密失败");
            return null;
        }
    }

    /**
     * 解密
     *
     * @param content 待解密内容
     * @param aesKey  密码
     * @return
     */
    public static String decrypt(String content, String aesKey) {
        if (StringUtils.isBlank(content)) {
            log.info("AES解密内容不能为空");
            return null;
        }
        if (StringUtils.isNotBlank(aesKey) && aesKey.length() == 16) { //判断秘钥是否为16位
            try {
                byte[] bytes = aesKey.getBytes(ENCODING);  //对密码进行编码
                SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);  //设置解密算法,生成秘钥
                Cipher cipher = Cipher.getInstance(CIPHER_PADDING); // "算法/模式/补码方式"
                cipher.init(Cipher.DECRYPT_MODE, skeySpec); //选择解密
                byte[] decodeBase64 = Base64Utils.decodeFromString(content);  //先进行Base64解码
                byte[] decrypted = cipher.doFinal(decodeBase64);  //根据待解密内容进行解密
                return new String(decrypted, ENCODING); //将字节数组转成字符串
            } catch (Exception e) {
                log.error("AES解密失败:",e);
                throw new RuntimeException(e.getMessage());
            }
        } else {
            log.info("AES解密失败");
            return null;
        }
    }

    /**
     * AES_CBC加密
     *
     * @param content 待加密内容
     * @param aesKey  密码
     * @return
     */
    public static String encryptCBC(String content, String aesKey) {
        if (StringUtils.isBlank(content)) {
            log.info("AES_CBC加密内容不能为空");
            return null;
        }
        if (StringUtils.isNotBlank(aesKey) && aesKey.length() == 16) {  //判断秘钥是否为16位
            try {
                byte[] bytes = aesKey.getBytes(ENCODING);  //对密码进行编码
                SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM); //设置加密算法,生成秘钥
                Cipher cipher = Cipher.getInstance(CIPHER_CBC_PADDING); // "算法/模式/补码方式"
                IvParameterSpec iv = new IvParameterSpec(SystemUtils.FILE_AES_IV_SEED_KEY.getBytes(ENCODING));  //偏移
                cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);  //选择加密
                byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));  //根据待加密内容生成字节数组
                return Base64Utils.encodeToString(encrypted); //返回base64字符串
            } catch (Exception e) {
                log.error("AES_CBC加密失败:",e);
                throw new RuntimeException(e.getMessage());
            }
        } else {
            log.info("AES_CBC加密失败");
            return null;
        }
    }

    /**
     * AES_CBC解密
     *
     * @param content 待解密内容
     * @param aesKey  密码
     * @return
     */
    public static String decryptCBC(String content, String aesKey) {
        if (StringUtils.isBlank(content)) {
            log.info("AES_CBC解密内容不能为空");
            return null;
        }
        //判断秘钥是否为16位
        if (StringUtils.isNotBlank(aesKey) && aesKey.length() == 16) {
            try {
                //对密码进行编码
                byte[] bytes = aesKey.getBytes(ENCODING);
                //设置解密算法,生成秘钥
                SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                //偏移
                IvParameterSpec iv = new IvParameterSpec(SystemUtils.FILE_AES_IV_SEED_KEY.getBytes(ENCODING));
                // "算法/模式/补码方式"
                Cipher cipher = Cipher.getInstance(CIPHER_CBC_PADDING);
                //选择解密
                cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

                //先进行Base64解码
                byte[] decodeBase64 = Base64Utils.decodeFromString(content);

                //根据待解密内容进行解密
                byte[] decrypted = cipher.doFinal(decodeBase64);
                //将字节数组转成字符串
                return new String(decrypted, ENCODING);
            } catch (Exception e) {
                log.error("AES_CBC解密失败",e);
                throw new
 RuntimeException(e.getMessage());
            }

        } else {
            log.info("AES_CBC解密失败");
            return null;
        }
    }
}

office文件转为pdf代码

/**
 * 文件格式转换工具类
 */
@Slf4j
public class FileConvertUtil {

    private FileConvertUtil() {
    }

    /**
     * office文档转换为PDF(处理本地文件)
     *
     * @param sourcePath 源文件路径
     * @param suffix     源文件后缀
     * @return InputStream 转换后文件输入流
     */
    public static InputStream convertLocaleFile(String sourcePath, String suffix) {
        try (InputStream inputStream = Files.newInputStream(Paths.get(sourcePath))) {
            return covertCommonByStream(inputStream, suffix);
        } catch (Exception e) {
            log.error("office文档转换为PDF(处理本地文件)失败:",e);
            throw new FileException(10000, e.getMessage());
        }
    }

    /**
     * office文档转换为PDF(处理网络文件)
     *
     * @param netFileUrl 网络文件路径
     * @param suffix     文件后缀
     * @return InputStream 转换后文件输入流
     */
    public static InputStream convertNetFile(String netFileUrl, String suffix) {
        try {
            URL url = new URL(netFileUrl); // 创建URL
            URLConnection urlConn = url.openConnection(); // 试图连接并取得返回状态码
            urlConn.connect();
            HttpURLConnection httpConn = (HttpURLConnection) urlConn;
            int httpResult = httpConn.getResponseCode();
            if (httpResult == HttpURLConnection.HTTP_OK) {
                InputStream inputStream = urlConn.getInputStream();
                return covertCommonByStream(inputStream, suffix);
            }
            return null;
        } catch (Exception e) {
            log.error("office文档转换为PDF(处理网络文件)失败:",e);
            throw new FileException(10000, e.getMessage());
        }
    }

    /**
     * 方法描述  将文件以流的形式转换
     *
     * @param inputStream 源文件输入流
     * @param suffix      源文件后缀
     * @return InputStream 转换后文件输入流
     */
    public static InputStream covertCommonByStream(InputStream inputStream, String suffix) {
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            OpenOfficeConnection connection = new SocketOpenOfficeConnection(Integer.parseInt(SystemUtils.OPEN_OFFICE_PORT));
            connection.connect();
            DocumentConverter converter = new StreamOpenOfficeDocumentConverter(connection);
            DefaultDocumentFormatRegistry formatReg = new DefaultDocumentFormatRegistry();
            DocumentFormat targetFormat = formatReg.getFormatByFileExtension(MimeTypeEnum.PDF.getExtension());
            DocumentFormat sourceFormat = formatReg.getFormatByFileExtension(suffix);
            converter.convert(inputStream, sourceFormat, out, targetFormat);
            connection.disconnect();
            return outputStreamConvertInputStream(out);
        } catch (Exception e) {
            log.error("文件转换为流失败:",e);
            throw new FileException(10000, e.getMessage());
        }
    }

    /**
     * outputStream转inputStream
     */
    public static ByteArrayInputStream outputStreamConvertInputStream(final OutputStream out)  {
        ByteArrayOutputStream baos = (ByteArrayOutputStream) out;
        return new ByteArrayInputStream(baos.toByteArray());
    }
}

配置文件

# 文件加密密钥
file.aes.private.key=qcmbkwses77vhft4
# 偏移量
file.aes.iv.seed=1234567812345678
# OpenOffice端口号
open.office.port=8100
@Slf4j
public class SystemUtils {

    private SystemUtils(){}

    private static Properties prop = null;
    //初始化静态代码块
    static {
        prop = new Properties();
        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("system.properties");
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        try {
            prop.load(reader);
            initProperties(prop);
        } catch (IOException e) {
            log.info("获取配置文件失败:{}",e.getMessage());
        }
    }


    /**
     * 文件AES加密密钥
     */
    public static String FILE_AES_PRIVATE_KEY;


    /**
     * 偏移量(CBC中使用,增强加密算法强度)
     */
    public static String FILE_AES_IV_SEED_KEY;


    public static String OPEN_OFFICE_PORT;


    private static void initProperties(Properties properties) {
        String fileAesKey = properties.getProperty("file.aes.private.key");
        if (StringUtils.isNotBlank(fileAesKey)) {
            FILE_AES_PRIVATE_KEY = fileAesKey;
        } else {
           log.error("=======获取文件加密AES密钥失败=======");
        }

        String fileAesIvSeedKey = properties.getProperty("file.aes.iv.seed");
        if (StringUtils.isNotBlank(fileAesIvSeedKey)) {
            FILE_AES_IV_SEED_KEY = fileAesIvSeedKey;
        } else {
            log.error("=======获取文件加密AES偏移量失败========");
        }

        String openOfficePort = properties.getProperty("open.office.port");
        if (StringUtils.isNotBlank(openOfficePort)) {
            OPEN_OFFICE_PORT = openOfficePort;
        } else {
            log.error("=======获取OpenOffice端口号失败========");
        }
    }
}

同理,图片也可以使用这种方法进行实现

提示:fileName这个参数是你服务器上存储PDF的文件地址
修改:之前的代码流没有关掉,现在修改了,然后如果你的pdf文件如果是保存在服务器上,这个fileName这个字段就是你服务器上存储PDF的文件地址,当然也可以做些处理,比如加密啥的,只要能保证解析到这个pdf文件就行,最后这个是预览pdf的地址,如果有需求要预览连接的,就自己在写业务需求的时候把这个连接拼接一下就行了,然后返回给前端
因为有时候需要pdf在页面嵌入展示,因此这边增加了一个可以在页面嵌入展示的方法,原理就是将pdf转为base64的格式,方便页面展示

后面加入了office文件的在线预览,其作用就是将其文件转为pdf流,使用的openoffice,但是这个需要安装软件,比较麻烦,而且,这边测试发现是支持2003版本的文档格式,后面这边看看还有其他好的方法没有,会再此更新

  • 1
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fly狮子座

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值