如何将文档、视频某页或某帧转换成图片?

目录

一、需求背景

二、引入依赖

三、根据自身的业务编写合适的代码


一、需求背景

        博主的大概需求是,获取第一章第一节的课件(有pdf、各种文档、视频等形式),生成课程的封面图片。

二、引入依赖
 

        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>1.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp</artifactId>
            <version>1.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco.javacpp-presets</groupId>
            <artifactId>ffmpeg-platform</artifactId>
            <version>3.4.2-1.4.1</version>
        </dependency>

三、根据自身的业务编写合适的代码

/**
     * 保存课程信息自动设置课程封面图片
     * @param courseInformation
     * @param coursewareId
     */
    private void setDefaultCourseCoverPhoto(CourseInformation courseInformation, Long coursewareId) {
        String key = "";
        InputStream pdfStream = null;
        PDDocument pdfDocument = null;
        try {
            CoursewareLibrary library = coursewareLibraryMapper.selectById(coursewareId);
            //获取网络流
            String urlByPreviewPdf = documentManagementApiServer.getUrlByPreviewPdf(library.getSourceFileKey());
            pdfStream = ImageUtil.getImageStream(urlByPreviewPdf);
            if (BusinessConstant.COURSEWARE_LIBRARY_VIDEO.equals(library.getCoursewareType()) ||
                    BusinessConstant.COURSEWARE_LIBRARY_AUDIO.equals(library.getCoursewareType())){
                pdfStream = returnBitMap(urlByPreviewPdf);
                //1.生成本地源文件、目的文件
                String source = tempPath + "/source/" + library.getSourceFileKey();
                MultipartFile multipartFile = FileUtil.getMultipartFile(pdfStream, library.getSourceFileKey());
                File file = FileUtil.toFile(multipartFile);
                BufferedImage buffImage = VideoCaptureUtils.generatePhoto(source,file.getPath());
                MultipartFile uploadMultipartFile = fileCase(buffImage);
                key = uploadPhoto(uploadMultipartFile);
                FileUtil.del(file);
                FileUtil.del(source);
            }else if (BusinessConstant.COURSEWARE_LIBRARY_FILE.equals(library.getCoursewareType()) ||
                    BusinessConstant.COURSEWARE_LIBRARY_PDF.equals(library.getCoursewareType())){
                pdfDocument = PDDocument.load(pdfStream);
                //PDFRenderer的类将PDF文档呈现为AWT BufferedImage
                PDFRenderer pdfRenderer = new PDFRenderer(pdfDocument);
                // 提取的页码
                int pageNumber = 0;
                // 以300 dpi 读取存入 BufferedImage 对象
                int dpi = 500;
                //Renderer类的renderImage()方法在特定页面中渲染图像
                BufferedImage buffImage = pdfRenderer.renderImageWithDPI(pageNumber, dpi, ImageType.RGB);
                // 文件类型转换
                MultipartFile multipartFile = fileCase(buffImage);
                key = uploadPhoto(multipartFile);
                // 关闭文档
                pdfDocument.close();
                //删除临时文件
//                        log.info("临时文件的目录:"+s);
            }
            courseInformation.setCoverPhotoKey(key);
        }catch (Exception e){
            log.error("{}",e);
            e.printStackTrace();
        }finally {
            IOUtils.closeQuietly(pdfStream);
            IOUtils.closeQuietly(pdfDocument);
        }
    }
    public static MultipartFile getMultipartFile(InputStream inputStream, String fileName) {
        FileItem fileItem = createFileItem(inputStream, fileName);
        return new CommonsMultipartFile(fileItem);
    }


    public static File toFile(MultipartFile multipartFile) {
        String fileName = multipartFile.getOriginalFilename();
        String prefix = "." + getExtensionName(fileName);
        File file = null;

        try {
            file = File.createTempFile(IdUtil.simpleUUID(), prefix);
            multipartFile.transferTo(file);
        } catch (IOException var5) {
            var5.printStackTrace();
        }

        return file;
    }


    /**
     * 将buffImage转换为MultipartFile
     * @param buffImage
     * @return MultipartFile
     */
    private MultipartFile fileCase(BufferedImage buffImage) {
        //得到BufferedImage对象
        // BufferedImage bufferedImage = JoinTwoImage.testEncode(200, 200, url);
        MultipartFile multipartFile= null;
        try {
            //创建一个ByteArrayOutputStream
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            //把BufferedImage写入ByteArrayOutputStream
            ImageIO.write(buffImage, "jpg", os);
            //ByteArrayOutputStream转成InputStream
            InputStream input = new ByteArrayInputStream(os.toByteArray());
            //InputStream转成MultipartFile
            multipartFile =new MockMultipartFile("file", "file.jpg", "text/plain", input);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return multipartFile;
    }
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class VideoCaptureUtils {

    /**
     *
     * @Title: getTempPath
     * @Description: 生成视频的首帧图片方法
     * @author: Zing
     * @param: @param tempPath 生成首帧图片的文件地址
     * @param: @param filePath 传进来的线上文件
     * @param: @return
     * @param: @throws Exception
     * @return: boolean
     * @throws
     */
    public static BufferedImage generatePhoto(String tempPath, String filePath) throws Exception {
        File targetFile = new File(tempPath);
        if (!targetFile.getParentFile().exists()) {
            targetFile.getParentFile().mkdirs();
        }
        File file2 = new File(filePath);
        BufferedImage bufferedImage = null;
        //判断文件是否为视频
        if(isVideo(filePath)) {
            if (file2.getParentFile().exists()) {
                FFmpegFrameGrabber ff = new FFmpegFrameGrabber(file2);
                ff.start();
                int ftp = ff.getLengthInFrames();
                int flag=0;
                Frame frame = null;
                while (flag <= ftp) {
                    //获取帧
                    frame = ff.grabImage();
                    //过滤前3帧,避免出现全黑图片
                    if ((flag>3)&&(frame != null)) {
                        break;
                    }
                    flag++;
                }
//                if(ImageIO.write(FrameToBufferedImage(frame), "jpg", targetFile)) {
//                    ff.close();
//                    ff.stop();
//                    return true;
//                }else {
//                    ff.close();
//                    ff.stop();
//                    return false;
//                }
                bufferedImage = FrameToBufferedImage(frame);
                ImageIO.write(bufferedImage, "jpg", targetFile);
                ff.close();
                ff.stop();
            }
        }
        return bufferedImage;
    }

    /***
     *
     * @Title: FrameToBufferedImage
     * @Description: 创建格式化BufferedImage对象
     * @author: Zing
     * @param: @param frame
     * @param: @return
     * @return: RenderedImage
     * @throws
     */
    private static BufferedImage FrameToBufferedImage(Frame frame) {
        //创建BufferedImage对象
        Java2DFrameConverter converter = new Java2DFrameConverter();
        BufferedImage bufferedImage = converter.getBufferedImage(frame);
        return bufferedImage;
    }

    /**
     *
     * @Title: isVideo
     * @Description:判断是不是视频
     * @author: Zing
     * @param: @param path 文件路径
     * @param: @return
     * @return: boolean       true是视频 false非视频
     * @throws
     */
    public static boolean isVideo(String path) {
        List<String> typeList = new ArrayList<String>();
        typeList.add("mp4");
        typeList.add("flv");
        typeList.add("avi");
        typeList.add("rmvb");
        typeList.add("rm");
        typeList.add("wmv");
        //获取文件名和后缀
        String suffix = path.substring(path.lastIndexOf(".") + 1);
        for(String type : typeList) {
            if(type.equalsIgnoreCase(suffix)) {
                return true;
            }
        }
        return false;
    }

    public static BufferedImage resize(BufferedImage img, int newW, int newH) {
        int w = img.getWidth();
        int h = img.getHeight();
        BufferedImage dimg = new BufferedImage(newW, newH, img.getType());
        Graphics2D g = dimg.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.drawImage(img, 0, 0, newW, newH, 0, 0, w, h, null);
        g.dispose();
        return dimg;
    }
}

各位可以关注下面这个公众号,这个公众号会分享一些相关的技术型的文章或者开发遇到的常见问题。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/55718.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI绘画:当艺术遇见智能

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 前言 随着人工智能技术…

接口自动化测试-Postman+Newman+Git+Jenkins实战集成(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、Postman 创建…

[语义分割] LR-ASPP(MobileNet v3、轻量化、16倍下采样、膨胀卷积、ASPP、SE)

Searching for MobileNetV3 论文地址&#xff1a;Searching for MobileNetV3Pytorch 实现代码&#xff1a; https://github.com/WZMIAOMIAO/deep-learning-for-image-processing/tree/master/pytorch_segmentation/lrasppMobileNet v3 LR-ASPP 这篇论文就是 MobileNet v3 的论…

golang interface类型的nil

golang中interface变量&#xff0c;底层两个对象来存&#xff0c;一个是type、一个是value&#xff0c;只有type、value都为nil时&#xff0c;interface变量才是nil package mainimport ("fmt""reflect" )type People interface {Show() }type Student str…

【数据结构】带头+双向+循环链表(DList)(增、删、查、改)详解

一、带头双向循环链表的定义和结构 1、定义 带头双向循环链表&#xff0c;有一个数据域和两个指针域。一个是前驱指针&#xff0c;指向其前一个节点&#xff1b;一个是后继指针&#xff0c;指向其后一个节点。 // 定义双向链表的节点 typedef struct ListNode {LTDataType dat…

LeetCode[面试题04.08]首个共同祖先

难度&#xff1a;Medium 题目&#xff1a; 设计并实现一个算法&#xff0c;找出二叉树中某两个节点的第一个共同祖先。不得将其他的节点存储在另外的数据结构中。注意&#xff1a;这不一定是二叉搜索树。 例如&#xff0c;给定如下二叉树: root [3,5,1,6,2,0,8,null,null,7,…

Shiro框架基本使用

一、创建maven项目&#xff0c;引入依赖 <dependencies><dependency><groupId>org.apache.directory.studio</groupId><artifactId>org.apache.commons.codec</artifactId><version>1.8</version></dependency><!-- …

【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群指令分析—上篇)

探究Redis服务启动的过程机制的技术原理和流程分析的指南&#xff08;Redis集群管理&#xff09; Redis集群管理查看集群中各个节点状态集群(cluster)cluster info的执行效果指令结果分析 cluster nodes的执行效果指令结果分析 节点(node)CLUSTER MEETCLUSTER FORGETCLUSTER RE…

Excel透视表与python实现

目录 一、Excel透视表 1、源数据 2、数据总分析 3、数据top分析 二、python实现 1、第一张表演示 2、第二张表演示 一、Excel透视表 1、源数据 1&#xff09;四个类目&#xff0c;每类50条数据 2&#xff09;数据内容 2、数据总分析 1&#xff09;选择要分析的字段&…

TCP的三次握手以及四次断开

TCP的三次握手和四次断开&#xff0c;就是TCP通信建立连接以及断开的过程 目录 【1】TCP的三次握手过程 ---- TCP建立连接的过程 【2】TCP的四次挥手 ---- TCP会话的断开 注意&#xff1a; 【1】TCP的三次握手过程 ---- TCP建立连接的过程 三次握手的过程&#xff1a…

TPC-DS 标准介绍、工具下载地址

目录 一、TPC-DS标准介绍 1. DMS介绍 2. TCP-DS概念 二、数据库模型 1. 数据库模型介绍 2. 数据库模型包含内容 三、数据生成器 1. 数据生成器介绍 2. 数据生成器包含内容 四、查询集合 1. 查询集合介绍 2. 查询集合包含的88个标准化查询和17个基准统计函数 五、性…

easyui实用点

easyui实用点 1.下拉框&#xff08;input框只能选不能手动输入编辑&#xff09; data-options"editable:false"//不可编辑2.日期框&#xff0c;下拉框&#xff0c;文本框等class class"easyui-datebox"//不带时分秒 class"easyui-datetimebox"…

【C++】C++入门

1.C关键字 2.命名空间 变量、函数和后面学到的类都是大量存在的&#xff0c;这些变量、函数和名称都将存在于全局作用域中&#xff0c;可能会导致一些冲突&#xff0c;比如命名冲突。使用命名空间的目的是对标识符的名称进行本地化&#xff0c;以避免命名冲突和名字污染。 2.1…

Oracle设置某个表字段递增

当Oracle设置字段递增创建触发器 先建一个序列&#xff0c;打开PLSQL 找到Sequences&#xff0c;右击新建 根据自己的需要填写 然后添加触发器&#xff0c;点新建-程序窗口-空白 --TEST_ID为触发器的名字&#xff0c;TEST是添加触发器的表名 CREATE OR REPLACE TRIGGER &qu…

【Ubuntu 18.04 搭建 DHCP 服务】

参考Ubuntu官方文档&#xff1a;https://ubuntu.com/server/docs/how-to-install-and-configure-isc-dhcp-server dhcpd.conf 手册页 配置&#xff1a;https://maas.io/docs/about-dhcp 实验环境规划 Ubuntu 18.04&#xff08;172.16.65.128/24&#xff09;dhcp服务端Ubuntu…

记录--一个好用的轮子 turn.js 实现仿真翻书的效果

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 国际惯例&#xff0c;官网链接 官网传送门 Github地址 github上有几个demos例子&#xff0c;介绍了基础用法。 我参考官网的例子&#xff0c;写了一个demo示例 安装 turn.js 依赖 jquery 库&#xff0…

InnoDB引擎底层逻辑讲解——架构之磁盘架构

1. System Tablespaces区域 系统表空间是change buffer&#xff08;更改缓冲区&#xff09;的存放区域&#xff0c;这是在8.0之后重新规划的&#xff0c;在5.x版本的时候&#xff0c;系统表空间还会存放innodb的数据字典undolog日志等信息&#xff0c;在8.0之后主要主要存放更…

APP开发入门:了解主流的编程语言

在过去的几年里&#xff0c;有许多程序员开始学习和使用编程语言。这其中包括C、C、 Java和 Python。尽管有许多语言可供选择&#xff0c;但大多数程序员都会选择最容易学习的编程语言。 如今&#xff0c;有很多编程语言供选择。程序员们在学习这些语言时可以自由地选择他们喜…

原子操作的重要性

原子操作&#xff1a;要么不做&#xff0c;要么一次性做完 非原子操作 其实ABCD都是对的。 B选项&#xff1a;正常执行&#xff0c;I线程执行2条语句全部执行完毕,再执行II线程重新执行一遍foo函数。 C选项&#xff1a;先执行I线程foo函数第一行代码&#xff0c;然后跳转执行…

蓝牙、GPS定位学习

启动状态&#xff08;APP&#xff09; 冷启动 指在启动应用时&#xff0c;后台没有应用的进程或者进程被杀死的情况下&#xff0c;系统会重新创建一个新的进程&#xff0c;并按照一定的顺序创建和初始化Application类和MainActivity类&#xff0c;最后显示在界面上。这个过程需…