根据视频链接自动获取视频封面
·
安装FFmpeg步骤(Linux服务器)
CentOS 7
sudo yum install epel-release
sudo rpm -v --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm
sudo yum install ffmpeg ffmpeg-devel
Ubuntu/Debian
sudo apt-get update
sudo apt-get install ffmpeg
验证安装
ffmpeg -version
安装FFmpeg步骤(Windows服务器)
下载FFmpeg
访问官网下载Windows版本:
https://ffmpeg.org/download.html
或直接下载完整版:
https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z
解压文件
将下载的压缩包解压,重命名为ffmpeg,并复制到C:\目录下,最终路径为C:\ffmpeg。
添加环境变量
- 打开系统属性(Win + R,输入
sysdm.cpl回车) - 进入"高级系统设置" → “环境变量”
- 编辑系统变量中的
Path,添加新路径:C:\ffmpeg\bin - 保存所有窗口
验证安装
打开新的命令提示符窗口,运行:
ffmpeg -version
视频封面处理代码实现
核心方法
public Map<String, Object> processVideoCover(String videoUrl) {
Map<String, Object> result = new HashMap<>();
File tempVideoFile = null;
File coverImageFile = null;
try {
// 下载视频到临时文件
tempVideoFile = downloadVideoToTemp(videoUrl);
if (tempVideoFile == null || !tempVideoFile.exists()) {
result.put("status", "error");
result.put("message", "视频下载失败");
return result;
}
// 提取封面图
coverImageFile = extractCoverWithFFmpegCommand(tempVideoFile);
if (coverImageFile == null || !coverImageFile.exists()) {
coverImageFile = extractCoverWithFFmpegAlternative(tempVideoFile);
}
if (coverImageFile == null || !coverImageFile.exists()) {
result.put("status", "error");
result.put("message", "封面图提取失败");
return result;
}
// 上传到OSS
Map<String, Object> ossResult = uploadCoverToOSS(coverImageFile);
if (ossResult != null && ossResult.containsKey("filePath")) {
result.put("status", "success");
result.put("message", "封面图处理成功");
result.put("coverUrl", ossResult.get("filePath"));
result.put("fileName", ossResult.get("fileName"));
result.put("videoUrl", videoUrl);
result.put("coverSize", coverImageFile.length());
System.out.println("封面图上传成功: " + ossResult.get("filePath"));
} else {
result.put("status", "error");
result.put("message", "封面图上传到OSS失败");
}
} catch (Exception e) {
result.put("status", "error");
result.put("message", "处理错误: " + e.getMessage());
} finally {
// 清理临时文件
if (tempVideoFile != null) tempVideoFile.delete();
if (coverImageFile != null) coverImageFile.delete();
}
return result;
}
视频下载方法
private File downloadVideoToTemp(String videoUrl) throws IOException {
String tempFileName = "video_" + System.currentTimeMillis() + "_" +
Math.abs(videoUrl.hashCode()) + ".mp4";
File tempFile = new File(System.getProperty("java.io.tmpdir"), tempFileName);
HttpURLConnection conn = null;
InputStream in = null;
FileOutputStream out = null;
try {
URL url = new URL(videoUrl);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
in = conn.getInputStream();
out = new FileOutputStream(tempFile);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
return tempFile;
} finally {
if (in != null) in.close();
if (out != null) out.close();
if (conn != null) conn.disconnect();
}
}
使用FFmpeg命令提取封面
private File extractCoverWithFFmpegCommand(File videoFile) {
try {
// 生成封面文件名
String coverFileName = "cover_" + System.currentTimeMillis() + ".jpg";
File coverFile = new File(System.getProperty("java.io.tmpdir"), coverFileName);
// CentOS系统FFmpeg路径 - 使用系统PATH中的ffmpeg
// 如果安装了多个版本,可以指定完整路径
String ffmpegCommand = "ffmpeg"; // 直接使用命令,系统会从PATH中查找
// 也可以使用完整路径(如果知道确切位置)
// String ffmpegCommand = "/usr/bin/ffmpeg";
// 或者使用 which ffmpeg 查看具体位置:which ffmpeg
// 构建FFmpeg命令 - 针对Linux系统优化
// 注意:Linux下命令参数与Windows基本相同,但路径分隔符不同
String[] cmd = {
ffmpegCommand,
"-i", videoFile.getAbsolutePath(), // 输入文件
"-ss", "00:00:01.000", // 跳转到第1秒
"-vframes", "1", // 抓取1帧
"-q:v", "2", // 图片质量
"-vf", "scale='if(gt(iw,ih),1280,-1)':'if(gt(iw,ih),-1,720)'", // 缩放
"-y", // 覆盖输出
coverFile.getAbsolutePath() // 输出文件
};
System.out.println("执行FFmpeg命令: " + String.join(" ", cmd));
// 在Linux上执行命令,可能需要设置环境变量
ProcessBuilder pb = new ProcessBuilder(cmd);
// 设置工作目录(可选)
pb.directory(new File(System.getProperty("java.io.tmpdir")));
// 合并标准输出和错误输出,方便读取
pb.redirectErrorStream(true);
// 执行命令
Process process = pb.start();
// 读取命令输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder output = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
System.out.println("FFmpeg输出: " + line);
}
// 等待命令执行完成
int exitCode = process.waitFor();
System.out.println("FFmpeg命令执行完成,退出码: " + exitCode);
if (exitCode != 0) {
System.out.println("FFmpeg命令执行失败,输出:\n" + output.toString());
// 检查是否是常见问题
if (output.toString().contains("command not found")) {
System.out.println("FFmpeg未安装或不在PATH中");
System.out.println("请执行: which ffmpeg 查看FFmpeg位置");
} else if (output.toString().contains("Permission denied")) {
System.out.println("权限被拒绝,请检查FFmpeg执行权限");
System.out.println("尝试执行: chmod +x /usr/bin/ffmpeg");
} else if (output.toString().contains("Invalid data found")) {
System.out.println("视频文件格式可能不支持");
}
return null;
}
// 检查生成的封面文件
if (!coverFile.exists()) {
System.out.println("封面文件不存在");
System.out.println("临时目录: " + System.getProperty("java.io.tmpdir"));
System.out.println("期望的文件路径: " + coverFile.getAbsolutePath());
// 列出临时目录内容,便于调试
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
String[] tmpFiles = tmpDir.list();
if (tmpFiles != null) {
System.out.println("临时目录内容:");
for (String file : tmpFiles) {
System.out.println(" " + file);
}
}
return null;
}
if (coverFile.length() == 0) {
System.out.println("封面文件存在但大小为0");
return null;
}
System.out.println("封面文件生成成功: " + coverFile.getAbsolutePath());
System.out.println("封面文件大小: " + coverFile.length() + " bytes");
return coverFile;
} catch (Exception e) {
System.out.println("执行FFmpeg命令失败: " + e.getMessage());
e.printStackTrace();
return null;
}
}
/**
* 使用FFmpeg命令提取封面(备选方案)
*/
private File extractCoverWithFFmpegAlternative(File videoFile) {
try {
// 生成封面文件名
String coverFileName = "cover_alt_" + System.currentTimeMillis() + ".jpg";
File coverFile = new File(System.getProperty("java.io.tmpdir"), coverFileName);
// 构建FFmpeg命令 - 方案2:使用更简单的参数
String[] cmd = {
"ffmpeg",
"-i", videoFile.getAbsolutePath(), // 输入文件
"-ss", "1", // 跳转到第1秒(简化格式)
"-vframes", "1", // 抓取1帧
"-f", "image2", // 输出格式为图片
"-y", // 覆盖输出文件
coverFile.getAbsolutePath() // 输出文件
};
System.out.println("执行FFmpeg备选命令: " + String.join(" ", cmd));
// 执行命令
Process process = Runtime.getRuntime().exec(cmd);
// 读取错误输出
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while (errorReader.readLine() != null) {
// 只读取不显示,避免日志过多
}
// 等待命令执行完成
int exitCode = process.waitFor();
System.out.println("FFmpeg备选命令执行完成,退出码: " + exitCode);
if (exitCode != 0) {
return null;
}
// 检查生成的封面文件
if (!coverFile.exists() || coverFile.length() == 0) {
return null;
}
System.out.println("备选方案封面文件生成成功: " + coverFile.getAbsolutePath());
return coverFile;
} catch (Exception e) {
System.out.println("执行FFmpeg备选命令失败: " + e.getMessage());
return null;
}
}
将封面图下载到OSS
private static Map<String, Object> uploadCoverToOSS(File coverFile) {
Map<String, Object> result = new HashMap<>();
try {
// OSS配置 - 请替换为您的实际配置
String endpoint = "**********";
String accessKeyId = "**********";
String accessKeySecret = "**********";
String bucketName = "**********";
// 生成文件名和路径
String fileName = "video_covers/" +
DateTimeUtil.getCurrentYear() + "/" +
DateTimeUtil.getCurrentMonth() + "/" +
DateTimeUtil.getCurrentDay() + "/" +
System.currentTimeMillis() + ".jpg";
// 创建OSS客户端
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 设置上传元信息
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType("image/jpeg");
metadata.setContentLength(coverFile.length());
metadata.setCacheControl("max-age=2592000"); // 缓存30天
// 上传文件
PutObjectRequest request = new PutObjectRequest(bucketName, fileName, coverFile);
request.setMetadata(metadata);
PutObjectResult putResult = ossClient.putObject(request);
// 构造访问URL
String fileUrl = "https://" + bucketName + ".*********/" + fileName;
result.put("filePath", fileUrl);
result.put("fileName", fileName);
result.put("ossRequestId", putResult.getRequestId());
ossClient.shutdown();
System.out.println("封面图上传到OSS成功: " + fileUrl);
return result;
} catch (Exception e) {
System.out.println("上传封面图到OSS失败: " + e.getMessage());
e.printStackTrace();
return null;
}
}
更多推荐



所有评论(0)