shenrongliang
2025-04-17 63a8f453cdd9d161fda1399256ce968154b3c394
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceUtil.java
@@ -43,6 +43,7 @@
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -696,7 +697,7 @@
    }
    @Async
    public CommonResult createCompositeVideo(CourseMediaSubtitlesReqVO courseMediaSubtitlesReqVO) {
    public void createCompositeVideo(CourseMediaSubtitlesReqVO courseMediaSubtitlesReqVO) {
        //片头地址
        String titles = courseMediaSubtitlesReqVO.getTitles();
        titles = configApi.getConfigValueByKey("easegen.url") + titles.substring(titles.lastIndexOf("/"));
@@ -734,30 +735,49 @@
        //去掉updateReqVO.getName()中的空格和特殊字符
        String newFileName = courseMediaSubtitlesReqVO.getCourseName().replaceAll("[\\s\\p{Punct}]", "");
        ProcessBuilder builder = new ProcessBuilder(
                "ffmpeg", "-f", "concat", "-safe","0", "-i",fileListPath , "-c", "copy", configApi.getConfigValueByKey(HEYGEM_FACE2FACE)+"/compositeVideo/"+newFileName+".mp4"
//                q
                "ffmpeg", "-f", "concat", "-safe", "0", "-i", fileListPath, "-c", "copy",
                configApi.getConfigValueByKey(HEYGEM_FACE2FACE) + "/compositeVideo/" + newFileName + ".mp4"
        );
        builder.redirectErrorStream(true);
        Process process = null;
        try {
            process = builder.start();
            // 读取 FFmpeg 输出(可选)
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            System.out.println(builder.command());
            System.out.println("最终视频已生成");
        } catch (IOException e) {
            throw new RuntimeException(e);
            // 使用 try-with-resources 确保流关闭
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
            }
            // 等待 FFmpeg 进程完成
            int exitCode = process.waitFor();
            if (exitCode != 0) {
                throw new RuntimeException("FFmpeg 执行失败,退出码:" + exitCode);
            }
            System.out.println("最终视频已生成");
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException("FFmpeg 执行异常", e);
        } finally {
            // 确保 Process 的输入/错误流被关闭
            if (process != null) {
                try {
                    process.getInputStream().close();
                    process.getErrorStream().close();
                    process.getOutputStream().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        byte[] bytes = FileUtil.readBytes(FileUtil.file(configApi.getConfigValueByKey(HEYGEM_FACE2FACE) +"/compositeVideo/"+newFileName+".mp4"));
        String compositeVideo = fileApi.createFile(bytes);
        CourseMediaDO courseMediaDO = new CourseMediaDO();
        courseMediaDO.setId(courseMediaSubtitlesReqVO.getId());
        courseMediaDO.setCompositeVideo(compositeVideo);
        int i = courseMediaMapper.updateById(courseMediaDO);
        if (i>0){
            return CommonResult.success("视频合成成功");
        }
        return CommonResult.error(BAD_REQUEST.getCode(),"视频合成失败");
        courseMediaMapper.updateById(courseMediaDO);
        System.out.println();
    }
}
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java
@@ -8,6 +8,7 @@
import org.apache.tika.Tika;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
/**
@@ -65,12 +66,33 @@
        response.setContentType(contentType);
        // 针对 video 的特殊处理,解决视频地址在移动端播放的兼容性问题
        if (StrUtil.containsIgnoreCase(contentType, "video")) {
            response.setHeader("Content-Length", String.valueOf(content.length - 1));
            response.setHeader("Content-Range", String.valueOf(content.length - 1));
            long contentLength = content.length;
            response.setHeader("Content-Length", String.valueOf(contentLength));
            response.setHeader("Content-Range", "bytes 0-" + (contentLength - 1) + "/" + contentLength);
            response.setHeader("Accept-Ranges", "bytes");
        }
        // 输出附件
        IoUtil.write(response.getOutputStream(), false, content);
        OutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            IoUtil.write(outputStream, false, content);
        } catch (IOException e) {
            if ("Connection reset by peer".equals(e.getMessage())) {
                System.out.println("客户端中断连接: {}");
                System.out.println(e.getMessage());
            } else {
                throw e;
            }
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    System.out.println("关闭输出流失败: {}");
                    System.out.println(e.getMessage());
                }
            }
        }
    }
}