太原医疗网站建设跨境电商单页网站的详情页怎么做的
2026/4/5 6:18:54 网站建设 项目流程
太原医疗网站建设,跨境电商单页网站的详情页怎么做的,个人养老金制度出台有望年底,做垂直导购网站还行吗大家好#xff0c;我是小悟。 第一部分#xff1a;认识 FFmpeg —— 视频界的瑞士军刀 FFmpeg 是什么#xff1f;想象一下#xff0c;如果你有一个朋友#xff0c;他能#xff1a; 把 MP4 变成 AVI#xff0c;就像把咖啡变成奶茶裁剪视频#xff0c;比理发师剪头发还精…大家好我是小悟。第一部分认识 FFmpeg —— 视频界的瑞士军刀FFmpeg 是什么想象一下如果你有一个朋友他能把 MP4 变成 AVI就像把咖啡变成奶茶裁剪视频比理发师剪头发还精准提取音频比从披萨上分离芝士还干净压缩视频比你把行李箱塞满时还高效这个“万能朋友”就是 FFmpeg它是一个开源的声音/影像处理工具功能强大到能让好莱坞特效师失业开玩笑的。# FFmpeg 的基本心态 # 给我一个视频我能还你一个世界 # 实际上它想说的是ffmpeg -i input.mp4 [一堆参数] output.mp4第二部分整合步骤 —— 像组装乐高一样简单步骤1先给项目来点“开胃菜”—— Maven依赖!-- pom.xml -- dependencies !-- SpringBoot 标准配置 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 让我们记录FFmpeg的“精彩表演” -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-logging/artifactId /dependency !-- 视频处理时的“后悔药”——异常处理 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency /dependencies步骤2配置FFmpeg —— 像教AI用筷子import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; Configuration ConfigurationProperties(prefix ffmpeg) Data public class FFmpegConfig { /** * FFmpeg可执行文件路径 * Windows: C:/ffmpeg/bin/ffmpeg.exe * Linux/Mac: /usr/bin/ffmpeg */ private String path; /** * 超时时间秒 * 防止视频处理变成“永恒等待” */ private Long timeout 3600L; /** * 线程数 * 多线程就像多双手干活更快 */ private Integer threads 4; }# application.yml ffmpeg: path: /usr/local/bin/ffmpeg # 你的FFmpeg安装路径 timeout: 3600 # 1小时足够看一集电视剧了 threads: 4 # 4个线程四核处理器的最爱步骤3创建FFmpeg指挥官import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; Slf4j Component public class FFmpegCommander { Autowired private FFmpegConfig ffmpegConfig; /** * 执行FFmpeg命令 * param commands 命令参数像给厨师递菜单 * return 是否成功厨子有没有把菜做糊 */ public boolean execute(ListString commands) { ListString fullCommand new ArrayList(); fullCommand.add(ffmpegConfig.getPath()); fullCommand.addAll(commands); log.info(FFmpeg开始干活啦命令{}, String.join( , fullCommand)); ProcessBuilder processBuilder new ProcessBuilder(fullCommand); processBuilder.redirectErrorStream(true); // 错误输出也给我看看 try { Process process processBuilder.start(); // 读取输出防止FFmpeg“自言自语”没人听 try (BufferedReader reader new BufferedReader( new InputStreamReader(process.getInputStream()))) { String line; while ((line reader.readLine()) ! null) { log.debug(FFmpeg悄悄说{}, line); } } // 等待处理完成别急着催 int exitCode process.waitFor(); boolean success exitCode 0; if (success) { log.info(FFmpeg完美收工); } else { log.error(FFmpeg罢工了退出码{}, exitCode); } return success; } catch (Exception e) { log.error(FFmpeg崩溃了原因{}, e.getMessage(), e); return false; } } /** * 获取FFmpeg版本验明正身 */ public String getVersion() { try { Process process new ProcessBuilder(ffmpegConfig.getPath(), -version).start(); BufferedReader reader new BufferedReader( new InputStreamReader(process.getInputStream())); return reader.readLine(); // 第一行就是版本信息 } catch (Exception e) { return FFmpeg可能去度假了 e.getMessage(); } } }步骤4创建视频处理服务 —— 你的私人视频管家import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import java.util.UUID; Slf4j Service public class VideoService { Autowired private FFmpegCommander ffmpegCommander; // 临时文件存放目录像快递的临时存放点 private final String TEMP_DIR System.getProperty(java.io.tmpdir) /video-process/; public VideoService() { // 确保临时目录存在 new File(TEMP_DIR).mkdirs(); } /** * 转换视频格式像把中文翻译成英文 * param inputFile 输入文件 * param targetFormat 目标格式mp4, avi, mov... */ public File convertFormat(MultipartFile inputFile, String targetFormat) throws IOException { log.info(开始格式转换{} → {}, getFileExtension(inputFile.getOriginalFilename()), targetFormat); // 1. 保存上传的文件像把食材先放到厨房 File input saveTempFile(inputFile); // 2. 准备输出文件准备好盘子 String outputFileName UUID.randomUUID() . targetFormat; File output new File(TEMP_DIR outputFileName); // 3. 构建FFmpeg命令菜单 ListString commands Arrays.asList( -i, input.getAbsolutePath(), // 输入文件 -threads, 4, // 用4个线程 -preset, fast, // 快速预设 -c:v, libx264, // 视频编码 -c:a, aac, // 音频编码 -y, // 覆盖输出文件别问我是否确定 output.getAbsolutePath() // 输出文件 ); // 4. 让FFmpeg大厨开始烹饪 boolean success ffmpegCommander.execute(commands); // 5. 清理临时文件洗盘子 input.delete(); if (success output.exists()) { log.info(格式转换成功文件大小{} MB, output.length() / (1024 * 1024)); return output; } else { throw new RuntimeException(转换失败FFmpeg可能去做美甲了); } } /** * 提取视频缩略图给视频拍证件照 */ public File extractThumbnail(MultipartFile videoFile, int second) throws IOException { log.info(正在给视频拍第{}秒的证件照..., second); File input saveTempFile(videoFile); String outputFileName UUID.randomUUID() .jpg; File output new File(TEMP_DIR outputFileName); ListString commands Arrays.asList( -i, input.getAbsolutePath(), -ss, String.valueOf(second), // 跳转到指定秒数 -vframes, 1, // 只要1帧 -vf, scale320:-1, // 缩放到宽度320高度自动 -y, output.getAbsolutePath() ); boolean success ffmpegCommander.execute(commands); input.delete(); if (success output.exists()) { log.info(缩略图生成成功); return output; } throw new RuntimeException(拍照失败视频可能害羞了); } /** * 压缩视频给视频减肥 */ public File compressVideo(MultipartFile videoFile, int targetBitrate) throws IOException { log.info(开始给视频减肥目标比特率{}k, targetBitrate); File input saveTempFile(videoFile); long originalSize input.length(); String outputFileName UUID.randomUUID() _compressed.mp4; File output new File(TEMP_DIR outputFileName); ListString commands Arrays.asList( -i, input.getAbsolutePath(), -threads, 4, -b:v, targetBitrate k, // 目标视频比特率 -b:a, 128k, // 音频比特率 -y, output.getAbsolutePath() ); boolean success ffmpegCommander.execute(commands); input.delete(); if (success output.exists()) { long compressedSize output.length(); double ratio (1.0 - (double)compressedSize/originalSize) * 100; log.info(减肥成功原大小{}MB现大小{}MB瘦身{:.1f}%, originalSize/(1024*1024), compressedSize/(1024*1024), ratio); return output; } throw new RuntimeException(减肥失败视频可能偷吃宵夜了); } /** * 合并视频和音频像给电影配音 */ public File mergeVideoAudio(MultipartFile videoFile, MultipartFile audioFile) throws IOException { log.info(开始给视频配音...); File video saveTempFile(videoFile); File audio saveTempFile(audioFile); String outputFileName UUID.randomUUID() _merged.mp4; File output new File(TEMP_DIR outputFileName); ListString commands Arrays.asList( -i, video.getAbsolutePath(), -i, audio.getAbsolutePath(), -c:v, copy, // 视频流直接复制不重新编码 -c:a, aac, // 音频重新编码 -map, 0:v:0, // 取第一个文件的视频 -map, 1:a:0, // 取第二个文件的音频 -shortest, // 以最短的流为准 -y, output.getAbsolutePath() ); boolean success ffmpegCommander.execute(commands); video.delete(); audio.delete(); if (success output.exists()) { log.info(配音成功新视频诞生了); return output; } throw new RuntimeException(合并失败可能视频和音频在闹离婚); } private File saveTempFile(MultipartFile file) throws IOException { String fileName UUID.randomUUID() _ file.getOriginalFilename(); Path path Paths.get(TEMP_DIR fileName); Files.copy(file.getInputStream(), path); return path.toFile(); } private String getFileExtension(String filename) { if (filename null) return unknown; int dotIndex filename.lastIndexOf(.); return (dotIndex -1) ? : filename.substring(dotIndex 1); } }步骤5创建控制器 —— 视频处理的接待处import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; Slf4j RestController RequestMapping(/api/video) public class VideoController { Autowired private VideoService videoService; Autowired private FFmpegCommander ffmpegCommander; GetMapping(/version) public String getFFmpegVersion() { String version ffmpegCommander.getVersion(); return {\version\: \ version \}; } PostMapping(/convert) public ResponseEntityResource convertFormat( RequestParam(file) MultipartFile file, RequestParam(format) String format, HttpServletResponse response) throws IOException { log.info(收到转换请求{} → {}, file.getOriginalFilename(), format); File converted videoService.convertFormat(file, format); return buildFileResponse(converted, converted. format, MediaType.APPLICATION_OCTET_STREAM); } PostMapping(/thumbnail) public ResponseEntityResource extractThumbnail( RequestParam(file) MultipartFile file, RequestParam(value second, defaultValue 5) int second) throws IOException { File thumbnail videoService.extractThumbnail(file, second); return buildFileResponse(thumbnail, thumbnail.jpg, MediaType.IMAGE_JPEG); } PostMapping(/compress) public ResponseEntityResource compressVideo( RequestParam(file) MultipartFile file, RequestParam(value bitrate, defaultValue 1000) int bitrate) throws IOException { File compressed videoService.compressVideo(file, bitrate); return buildFileResponse(compressed, compressed.mp4, MediaType.APPLICATION_OCTET_STREAM); } PostMapping(/merge) public ResponseEntityResource mergeVideoAudio( RequestParam(video) MultipartFile video, RequestParam(audio) MultipartFile audio) throws IOException { File merged videoService.mergeVideoAudio(video, audio); return buildFileResponse(merged, merged.mp4, MediaType.APPLICATION_OCTET_STREAM); } private ResponseEntityResource buildFileResponse(File file, String filename, MediaType mediaType) { if (!file.exists()) { return ResponseEntity.notFound().build(); } Resource resource new FileSystemResource(file); // 文件下载完成后自动删除深藏功与名 file.deleteOnExit(); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, attachment; filename\ filename \) .contentType(mediaType) .contentLength(file.length()) .body(resource); } }步骤6添加异常处理 —— 给程序买份保险import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.multipart.MaxUploadSizeExceededException; import java.util.HashMap; import java.util.Map; Slf4j RestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(Exception.class) public ResponseEntityMapString, Object handleException(Exception e) { log.error(系统闹情绪了{}, e.getMessage(), e); MapString, Object response new HashMap(); response.put(success, false); response.put(message, 服务器开小差了可能是FFmpeg在偷懒); response.put(error, e.getMessage()); response.put(timestamp, System.currentTimeMillis()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(response); } ExceptionHandler(MaxUploadSizeExceededException.class) public ResponseEntityMapString, Object handleMaxSizeException() { MapString, Object response new HashMap(); response.put(success, false); response.put(message, 文件太大了服务器拿不动了); response.put(suggestion, 请尝试压缩视频或上传小一点的文件); return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE) .body(response); } ExceptionHandler(IOException.class) public ResponseEntityMapString, Object handleIOException(IOException e) { MapString, Object response new HashMap(); response.put(success, false); response.put(message, 文件读写出了问题可能是磁盘在闹脾气); response.put(error, e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(response); } }第三部分使用示例 —— 让我们来实际操练一下1. 启动应用程序import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class VideoProcessingApplication { public static void main(String[] args) { SpringApplication.run(VideoProcessingApplication.class, args); System.out.println(视频处理服务启动成功); System.out.println(FFmpeg整装待发随时准备处理你的视频); } }2. 测试API使用Postman或curl测试# 查看FFmpeg版本 curl http://localhost:8080/api/video/version # 转换视频格式 curl -X POST -F fileinput.avi -F formatmp4 \ http://localhost:8080/api/video/convert --output output.mp4 # 提取缩略图 curl -X POST -F filevideo.mp4 -F second10 \ http://localhost:8080/api/video/thumbnail --output thumbnail.jpg # 压缩视频 curl -X POST -F filelarge_video.mp4 -F bitrate500 \ http://localhost:8080/api/video/compress --output compressed.mp4第四部分高级技巧 —— 让FFmpeg更懂你1. 添加进度监听给视频处理加个进度条public interface ProgressListener { void onProgress(double percentage, String message); void onComplete(File outputFile); void onError(String error); } // 在FFmpegCommander中添加进度解析 private void parseProgress(String line, ProgressListener listener) { // 解析FFmpeg的输出提取进度信息 // 示例输出frame 123 fps25.1 time00:00:04.92 bitrate 512.0kbits/s if (line.contains(time)) { // 这里可以解析时间计算进度百分比 // 实际实现需要根据视频总时长计算 } }2. 批量处理一次处理多个文件public ListFile batchConvert(ListMultipartFile files, String format) { return files.parallelStream() // 并行处理更快 .map(file - { try { return videoService.convertFormat(file, format); } catch (IOException e) { log.error(转换失败{}, file.getOriginalFilename(), e); return null; } }) .filter(Objects::nonNull) .collect(Collectors.toList()); }3. 视频信息提取给视频做体检public MapString, Object getVideoInfo(File videoFile) { // 使用FFprobeFFmpeg的小伙伴获取视频信息 ListString commands Arrays.asList( -v, error, -select_streams, v:0, -show_entries, streamwidth,height,duration,bit_rate,codec_name, -of, json, videoFile.getAbsolutePath() ); // 执行命令并解析JSON结果 // 返回包含分辨率、时长、码率、编码格式等信息 }第五部分总结与注意事项成功整合的秘诀正确安装FFmpeg确保系统PATH中有FFmpeg或者配置正确的路径# 检查安装 ffmpeg -version资源管理视频处理很吃内存记得给JVM足够的内存java -Xmx2g -jar your-application.jar及时清理临时文件防止磁盘被撑爆错误处理FFmpeg可能会因为各种原因失败不支持的格式、损坏的文件等添加重试机制和详细的日志记录安全性限制上传文件类型和大小对用户输入进行严格验证防止命令注入攻击可能遇到的坑跨平台问题Windows和Linux下的路径差异编码问题中文字符在命令中可能需要特殊处理权限问题确保应用有执行FFmpeg的权限性能问题大文件处理可能需要很长时间考虑异步处理为什么选择这个方案灵活性强可以执行任何FFmpeg支持的操作功能全面视频处理界的瑞士军刀社区支持好遇到问题容易找到解决方案免费开源省钱又省心最后FFmpeg就像一把强大的电锯——功能强大但需要小心使用。不要在生产环境直接运行未经验证的命令否则可能会把服务器CPU烧得像烤红薯让磁盘空间消失得比钱包里的钱还快产生一堆让你怀疑人生的临时文件但只要你按照本文的步骤像对待一只温顺的猫一样对待FFmpeg它就会成为你在视频处理领域最得力的助手祝你的视频处理之路顺畅无比就像FFmpeg处理一个10秒的GIF一样轻松谢谢你看我的文章既然看到这里了如果觉得不错随手点个赞、转发、在看三连吧感谢感谢。那我们下次再见。您的一键三连是我更新的最大动力谢谢山水有相逢来日皆可期谢谢阅读我们再会我手中的金箍棒上能通天下能探海

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询