io流转换为Multipart文件


    io流转换为Multipart文件

    个人的话是运用到了minio文件服务器保存文件,前端(vue)异步上传文件后,由于要提升用户体验效果,先上传文件到后台服务器,返回视频在文件服务器的link()参数,然后保存该文件到数据库,然后运用java 定时任务之一 @Scheduled注解(如何使用自行www.baidu.com,定时进行在后台转码相关视频文件。运用到的转码工具是FFmpeg.exe。

  详细如下:

  1.   新建一份工具类文件,进行转码操作(亲测转码各参数都有用),个人的话是统一转码为MP4文件。
package org.springblade.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.common.config.loadFileConfig;
import org.springblade.common.service.ResourceService;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.tool.api.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static jodd.io.FileUtil.deleteFile;


/**
 * Created by liuminghui on 2020/12/30 22:14
 *
 * @annotate:
 * @Version 1.0
 */
public class DisposeVideoUtils {
    private static final Logger logger = LoggerFactory.getLogger(DisposeVideoUtils.class);

    // 转码ffmpeg.exe工具路径
    private static final String FFMPEG_PATH = "F:\\gugexaizai\\acutvideo\\ffmpeg.exe";

    // 转码mencoder.exe工具路径
    private static final String MENCODER_PATH = "F:\\gugexaizai\\acutvideo\\mencoder.exe";

    // 临时的视频存储路径,转码完成后可删除
    private static final String TEMPORARY_VIDEO_PATH = "F:\\Images\\";

    // 转码成功mp4视频存放路径

    private static final String uploadFolder="F:\\ImagesPath\\";


    // 转码后台的视频访问路径
    private String videoUrl;

    // 视频大小
    private String size;
    private String filerealname;
    private String PATH;
    // 视频截图路径
    private String videoImg;

    public DisposeVideoUtils() {
    }

    //重构构造方法,传入视频存放路径
    public DisposeVideoUtils(String path) {
        videoUrl = path;
    }

    //set和get方法传递path
    public String getVideoPath() {
        return videoUrl;
    }

    public String getSize() {
        return size;
    }

    public void setSize(String size) {
        this.size = size;
    }

    public void setVideoPath(String path) {
        videoUrl = path;
    }

    public String getVideoImg() {
        return videoImg;
    }

    public void setVideoImg(String videoImg) {
        this.videoImg = videoImg;
    }

    /**
     * 转码、截图和删除源文件功能
     * @param tempPath  临时源视频文件路径
     */
//    public String runConver(String tempPath) {
//        logger.info("=================转码过程开始=====================");
//        try {
//            String targetExtension = ".mp4";    // 设置转换的格式
//            boolean isDelSourseFile = true;
//            // 转码、截图和删除源文件
//            boolean beginConver = beginVideoConver(tempPath, targetExtension, isDelSourseFile);
//            if (beginConver) {
//                logger.info("=================转码过程彻底结束=====================");
//                return videoUrl;
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return null;
//    }

    /**
     * 视频转码、截图和删除原视频处理
     * @param tempPath          临时源视频路径
     * @param targetExtension   转码成功的视频后缀名
     * @param isDelSourseFile   转换完成后是否删除源文件
     * @return
     */
//    private boolean beginVideoConver(String tempPath, String targetExtension, boolean isDelSourseFile) {
//        File file = new File(tempPath);
//        String fileName = file.getName();          //获取文件名+后缀名
//        String fileNameSuffix = fileName.substring(fileName.lastIndexOf(".") + 1);   // 获取需要转码的文件后缀
//        //执行转码机制
//        if (process(tempPath, fileNameSuffix, targetExtension)) {
//            //删除临时视频或转码原视频
//            if (isDelSourseFile) {
//                // 删除临时文件
//                File fileDelete = new File(TEMPORARY_VIDEO_PATH);
//                String[] tempList = fileDelete.list();
//                File temp = null;
//                for (int i = 0; i < tempList.length; i++) {
//                    if (TEMPORARY_VIDEO_PATH.endsWith(File.separator)) {
//                        temp = new File(TEMPORARY_VIDEO_PATH + tempList[i]);
//                    } else {
//                        temp = new File(TEMPORARY_VIDEO_PATH + File.separator + tempList[i]);
//                    }
//                    if (temp.isFile() || temp.isDirectory()) {
//                        temp.delete(); // 删除文件夹里面的文件
//                    }
//                }
//            }
//
//            return true;
//        } else {
//            return false;
//        }
//    }


    /**
     * 实际转换视频格式的方法
     * @param tempPath        临时源视频文件路径
     * @param fileNameSuffix  源视频后缀名
     * @param targetExtension 转码成功的视频后缀名
     * @return
     */
//    private boolean process(String tempPath, String fileNameSuffix, String targetExtension) {
//        //先判断视频的类型-返回状态码
//        int type = checkVideoSuffix(fileNameSuffix);
//        boolean status = false;
//
//        //根据状态码处理
//        if (type == 0) {
//            logger.info("ffmpeg可以转换,统一转为mp4文件");
//          String  status = processVideoFormat(tempPath, targetExtension);//可以指定转换为什么格式的视频
//        } else if (type == 1) {
//            //如果type为1,将其他文件先转换为avi,然后在用ffmpeg转换为指定格式
//            logger.info("ffmpeg不可以转换,先调用mencoder转码avi");
//            String avifilepath = processAVI(tempPath);
//
//            if (avifilepath == null){
//                // 转码失败--avi文件没有得到
//                logger.info("mencoder转码失败,未生成AVI文件");
//                return false;
//            }else {
//                logger.info("生成AVI文件成功,ffmpeg开始转码:");
//              String  status = processVideoFormat(avifilepath, targetExtension);
//            }
//        }
//        return status;   //执行完成返回布尔类型true
//    }

    /**
     * 转换为指定格式
     * ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
     * @param oldfilepath     临时源视频文件路径
     * @param targetExtension 转码成功的视频后缀名 .xxx
     * @return
     */
    public   String processVideoFormat(String oldfilepath, String targetExtension) {
        logger.info("调用了ffmpeg.exe工具");


        // 先确保保存转码后的视频的mp4文件夹存在:TRANSCODE_VIDEO_MP4PATH-保存路径
        String mp4Path = uploadFolder + Long.toString(new Date().getTime()) + targetExtension;
        File tempFile = new File(uploadFolder);
        if (tempFile.exists()) {
            if (tempFile.isDirectory()) {
                logger.info("该转码后的文件夹已存在");
            } else {
                logger.info("同名的转码后的文件存在,不能创建文件夹");
            }
        } else {
            // 创建目录
            if (tempFile.mkdirs()) {
                logger.info("创建目录转码后的文件夹:" + uploadFolder + ",成功!");
            } else {
                logger.info("创建目录转码后的文件夹:" + uploadFolder + ",失败!");
            }
        }
        List<String> commend = new ArrayList<String>();
        commend.add(FFMPEG_PATH);      // 添加转换工具路径
        commend.add("-i");             // 添加参数"-i",该参数指定要转换的文件
        commend.add(oldfilepath);      // 添加要转换格式的视频文件的路径
        commend.add("-vcodec");
        commend.add("libx264");
        commend.add("-acodec");
        commend.add("aac");
        commend.add("-ab");
        commend.add("128k");
        commend.add("-ar");
        commend.add("16k");
        commend.add("-ac");
        commend.add("2");
        commend.add("-f");            // 添加参数"-y",该参数指定将覆盖已存在的文件
        commend.add("mp4");            // 添加参数"-y",该参数指定将覆盖已存在的文件
        commend.add("-y");            // 添加参数"-y",该参数指定将覆盖已存在的文件
        commend.add(mp4Path);

        // 打印命令
        StringBuffer test = new StringBuffer();
        for (int i = 0; i < commend.size(); i++) {
            test.append(commend.get(i) + " ");
        }
        logger.info("ffmpeg输入的命令:" + test);

        try {
            String suffix = oldfilepath.substring(oldfilepath.lastIndexOf(".") + 1);
            if(checkVideoSuffix(suffix)==0){
                // 多线程处理加快速度-解决rmvb数据丢失builder名称要相同
                ProcessBuilder builder = new ProcessBuilder();
                builder.command(commend);
                builder.redirectErrorStream(true);
                Process process = builder.start(); // 多线程处理加快速度-解决数据丢失
                // 线程处理
                threadSyn(process);
                process.waitFor();      // 进程等待机制,必须要有,否则不生成mp4!!!
                logger.info("生成mp4视频为:{}", mp4Path);
                // 生成mp4视频保存路径
                setVideoPath(mp4Path);

                logger.info("===============视频转码结束=================");

                return mp4Path;
            }
            else if(checkVideoSuffix(suffix)==200){
                return oldfilepath;

            }
            else{
                return ("该文件暂不支持转换");
            }

        } catch (Exception e) {
            e.printStackTrace();
            return mp4Path;
        }
    }


    /**
     *  对ffmpeg无法解析的文件格式(wmv9,rm,rmvb等),
     *  可以先用(mencoder)转换为avi(ffmpeg能解析的)格式.再用ffmpeg解析为指定格式
     * @param oldfilepath   临时源视频文件路径
     * @return
     */
    private String processAVI(String oldfilepath) {
        logger.info("调用了mencoder.exe工具");
        String tempPath = TEMPORARY_VIDEO_PATH + Long.toString(new Date().getTime());
        List<String> commend = new ArrayList<String>();
        commend.add(MENCODER_PATH);        // 指定mencoder.exe工具的位置
        commend.add(oldfilepath);          // 指定源视频的位置
        commend.add("-oac");
        commend.add("mp3lame");            // lavc 原mp3lame
        commend.add("-lameopts");
        commend.add("preset=64");
        commend.add("-ovc");
        commend.add("xvid");               // mpg4(xvid),AVC(h.264/x264),只有h264才是公认的MP4标准编码,如果ck播放不了,就来调整这里
        commend.add("-xvidencopts");       // xvidencopts或x264encopts
        commend.add("bitrate=600");        // 600或440
        commend.add("-of");
        commend.add("avi");
        commend.add("-o");
        commend.add(tempPath + ".avi");    // 存放路径+名称,生成.avi视频

        // 打印出转换命令
        StringBuffer test = new StringBuffer();
        for (int i = 0; i < commend.size(); i++) {
            test.append(commend.get(i) + " ");
        }
        logger.info("mencoder输入的命令:" + test);
        try {
            // 调用线程命令启动转码
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commend);
            Process process = builder.start(); // 多线程处理加快速度-解决数据丢失
            // 线程处理
            threadSyn(process);
            // 等Mencoder进程转换结束,再调用ffmepg进程非常重要!!!
            process.waitFor();
            logger.info("Mencoder进程结束");
            return tempPath + ".avi"; // 返回转为AVI以后的视频地址

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 视频截图功能
     * @param sourceVideoPath 需要被截图的视频路径(包含文件名和后缀名)
     * @return
     */
    public boolean processImg(String sourceVideoPath) {
        String imageFile = "images/videoFile/";
        String fileImpPath = ResourceService.rb.getString("filePath");
        // 先确保保存截图的文件夹存在
        File tempFile = new File(fileImpPath + imageFile);
        if (tempFile.exists()) {
            if (tempFile.isDirectory()) {
                logger.info("该截图保存文件夹存在。");
            } else {
                logger.info("同名的截图保存文件存在,不能创建文件夹。");
            }
        } else {
            logger.info("截图保存文件夹不存在,创建该文件夹。");
            tempFile.mkdir();
        }

        File file = new File(sourceVideoPath);
        String fileName = file.getName();                  // 获取视频文件的名称。
        String fileRealName = fileName.substring(0, fileName.lastIndexOf("."));           // 获取不加后缀名的视频名
        imageFile = imageFile + fileRealName + ".jpg";
        List<String> commend = new ArrayList<String>();
        // 第一帧: 00:00:01
        // 截图命令:time ffmpeg -ss 00:00:01 -i test1.flv -f image2 -y test1.jpg
        commend.add(FFMPEG_PATH);          // 指定ffmpeg工具的路径
        commend.add("-ss");
        commend.add("00:00:03");           // 3是代表第3秒的时候截图
        commend.add("-i");
        commend.add(sourceVideoPath);      // 截图的视频路径
        commend.add("-f");
        commend.add("image2");
        commend.add("-y");
        commend.add(fileImpPath + imageFile); // 生成截图xxx.jpg

        // 打印截图命令
        StringBuffer test = new StringBuffer();
        for (int i = 0; i < commend.size(); i++) {
            test.append(commend.get(i) + " ");
        }
        logger.info("截图命令:" + test);

        // 转码后完成截图功能-还是得用线程来解决
        try {
            // 调用线程处理命令
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commend);
            Process process = builder.start();
            // 线程处理
            threadSyn(process);
            // 等Mencoder进程转换结束,再调用ffmepg进程非常重要!!!
            process.waitFor();
            setVideoImg(imageFile);
            // 视频大小
            String videoSize = FileUploadToolUtil.getSize(file);
            setSize(videoSize);
            logger.info("截图进程结束,视频大小:{}", getSize());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 多线程处理
     * @param process
     */
    public void threadSyn(Process process) {
        // 获取进程的标准输入流
        final InputStream is1 = process.getInputStream();
        // 获取进程的错误流
        final InputStream is2 = process.getErrorStream();
        // 启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流
        new Thread() {
            public void run() {
                BufferedReader br = new BufferedReader(new InputStreamReader(is1));
                try {
                    String lineB = null;
                    while ((lineB = br.readLine()) != null) {
                        if (lineB != null) {
                            logger.info(lineB); //必须取走线程信息避免堵塞
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                // 关闭流
                finally {
                    try {
                        is1.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }
        }.start();
        new Thread() {
            public void run() {
                BufferedReader br2 = new BufferedReader(new InputStreamReader(is2));
                try {
                    String lineC = null;
                    while ((lineC = br2.readLine()) != null) {
                        if (lineC != null) {
                            logger.info(lineC); //必须取走线程信息避免堵塞
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

                // 关闭流
                finally {
                    try {
                        is2.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }
        }.start();
    }


    /**
     * 检查文件类型,检查非MP4后缀
     * asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等使用ffmpeg能解析
     * wmv9,rm,rmvb等ffmpeg无法解析,先用别的工具(mencoder)转换为avi(ffmpeg能解析的)格式
     * @param suffixName 后缀名
     * @return
     */
    public int checkVideoSuffix(String suffixName) {
        if (suffixName.equals("avi")) {
            return 0;
        } else if (suffixName.equals("mpg")) {
            return 0;
        } else if (suffixName.equals("wmv")) {
            return 0;
        } else if (suffixName.equals("3gp")) {
            return 0;
        } else if (suffixName.equals("mov")) {
            return 0;
        } else if (suffixName.equals("asf")) {
            return 0;
        } else if (suffixName.equals("asx")) {
            return 0;
        } else if (suffixName.equals("flv")) {
            return 0;
        } else if (suffixName.equals("mkv")) {
            return 0;
        } else if (suffixName.equals("wmv9")) {
            return 1;
        } else if (suffixName.equals("rm")) {
            return 1;
        } else if (suffixName.equals("rmvb")) {
            return 1;
        } else if (suffixName.equals("mp4")) {     // MP4不转码
            return 200;
        }
        return 9;
    }


    /**
     * 视频写入本地磁盘/服务器
     * @param file         上传文件
     * @param filePath     存储位置
     * @param fileName     文件名称
     * @return
     */
    public boolean uploadVideo(MultipartFile file, String filePath, String fileName) {
        // 上传到本地磁盘/服务器
        try {
            logger.info("上传的视频写入本地磁盘/服务器");
            InputStream is = file.getInputStream();
            OutputStream os = new FileOutputStream(new File(filePath, fileName));
            int len = 0;
            byte[] buffer = new byte[2048];

            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
            os.close();
            os.flush();
            is.close();
            return true;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
//    public void run() {
//        try {
//            // 转换并截图
//            String filePath = "D:\\video\\old\\test.avi";
//            DisposeVideoUtils cv = new DisposeVideoUtils(filePath);
//            cv.beginConver();
//
//            // 仅截图
//            // ProcessFlvImg pfi = new ProcessFlvImg();
//            // pfi.processImg("D:\\video\\old\\test.avi");
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
private boolean processFLV(String oldfilepath) {

    if (!checkfile(PATH)) {
        System.out.println(oldfilepath + " is not file");
        return false;
    }

    List commend = new java.util.ArrayList();
    commend.add(FFMPEG_PATH);
    commend.add("-i");
    commend.add(oldfilepath);
    commend.add("-vcodec");
    commend.add("libx264");
    commend.add("-acodec");
    commend.add("aac");
    commend.add("-s");
    commend.add("1280x720");
    commend.add("-vprofile");
    commend.add("high");
//        commend.add("-vlevel");
//        commend.add("3.1");
//        commend.add("-coder");
//        commend.add("1");
//        -vcodec libx264  -acodec aac -ab 128k -ar 16k -ac 2 -f mp4 -y
//        commend.add("-movflags");
//        commend.add("faststart");
//        commend.add("-force_key_frames");
//        commend.add("1");
//        commend.add("-strict");
//        commend.add("experimental");
    commend.add("-r");
    commend.add("25");
    commend.add("-g");
    commend.add("30");
    commend.add("-bf");
    commend.add("2");
    commend.add("-ab");
    commend.add("128k");
    commend.add("-ar");
    commend.add("44100");
    commend.add("-ac");
    commend.add("2");
//        commend.add("-b:v");
//        commend.add("1.6M");
//        commend.add("-sc_threshold");
//        commend.add("0");
    commend.add("-f");            // 添加参数"-y",该参数指定将覆盖已存在的文件
    commend.add("mp4");            // 添加参数"-y",该参数指定将覆盖已存在的文件
    commend.add("-y");
    commend.add(uploadFolder + filerealname + ".mp4");
    try {
        ProcessBuilder builder = new ProcessBuilder();
        String cmd = commend.toString();
        builder.command(commend);
        Process p = builder.start();
        doWaitFor(p);
        p.destroy();
        deleteFile(oldfilepath);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}
    public void deleteFile(String filepath) {
        File file = new File(filepath);
        if (PATH.equals(filepath)) {
            if (file.delete()) {
                System.out.println("文件" + filepath + "已删除");
            }
        } else {
            if (file.delete()) {
                System.out.println("文件" + filepath + "已删除 ");
            }
            File filedelete2 = new File(PATH);
            if (filedelete2.delete()) {
                System.out.println("文件" + PATH + "已删除");
            }
        }
    }
    private boolean checkfile(String path) {
        File file = new File(path);
        if (!file.isFile()) {
            return false;
        } else {
            return true;
        }
    }
    public int doWaitFor(Process p) {
        InputStream in = null;
        InputStream err = null;
        int exitValue = -1; // returned to caller when p is finished
        try {
            System.out.println("comeing");
            in = p.getInputStream();
            err = p.getErrorStream();
            boolean finished = false; // Set to true when p is finished

            while (!finished) {
                try {
                    while (in.available() > 0) {
                        Character c = new Character((char) in.read());
                        System.out.print(c);
                    }
                    while (err.available() > 0) {
                        Character c = new Character((char) err.read());
                        System.out.print(c);
                    }

                    exitValue = p.exitValue();
                    finished = true;

                } catch (IllegalThreadStateException e) {
                    Thread.currentThread().sleep(500);
                }
            }
        } catch (Exception e) {
            System.err.println("doWaitFor();: unexpected exception - "
                    + e.getMessage());
        } finally {
            try {
                if (in != null) {
                    in.close();
                }

            } catch (IOException e) {
                System.out.println(e.getMessage());
            }
            if (err != null) {
                try {
                    err.close();
                } catch (IOException e) {
                    System.out.println(e.getMessage());
                }
            }
        }
        return exitValue;
    }



}

    2.  将视频文件转码保存在本地之后,因为没有前端操作,一切都是在后台默默的进行,所以我们要读取该文件(保存在了本地磁盘),这必将是通过io流读取文件并获得,但是由于minio的上传方法要求是Multipart文件,所以将io流转换为Multipart文件就可以了。

/**
     *
     * @Description io流转换为MultipartFile---------返回MultipartFile文件
     * @return org.springframework.web.multipart.MultipartFile
     * @date 2020/12/30
     * @auther liuminghui
     */
    public static MultipartFile getFile( String filePath) throws IOException {

        File file = new File(filePath);
        FileItem fileItem = new DiskFileItem("copyfile.txt", Files.probeContentType(file.toPath()),false,file.getName(),(int)file.length(),file.getParentFile());
        byte[] buffer = new byte[4096];
        int n;
        try (InputStream inputStream = new FileInputStream(file); OutputStream os = fileItem.getOutputStream()){
            while ( (n = inputStream.read(buffer,0,4096)) != -1){
                os.write(buffer,0,n);
            }
            //也可以用IOUtils.copy(inputStream,os);
            MultipartFile multipartFile = new CommonsMultipartFile(fileItem);
            System.out.println(multipartFile.getName());
            return multipartFile;
        }catch (IOException e){
            e.printStackTrace();
        }
        return null;
    }

    3,  大致就是这样,还有很多文件的参数的方法都可以在各大网站上搜索到的,这里纯属是记录一下工作中遇到的各种问题和最终解决,感谢平台!


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM