|
|
@@ -0,0 +1,329 @@
|
|
|
+package vip.xiaonuo.disk.util;
|
|
|
+
|
|
|
+import io.minio.*;
|
|
|
+import io.minio.errors.ErrorResponseException;
|
|
|
+import io.minio.errors.InvalidResponseException;
|
|
|
+import io.minio.errors.XmlParserException;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.*;
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+@Component
|
|
|
+public class VideoConverter {
|
|
|
+
|
|
|
+ // MinIO配置
|
|
|
+
|
|
|
+ @Value("${ufop.minio.endpoint}")
|
|
|
+ private String MINIO_ENDPOINT;
|
|
|
+ @Value("${ufop.minio.access-key}")
|
|
|
+ private String ACCESS_KEY;
|
|
|
+ @Value("${ufop.minio.secret-key}")
|
|
|
+ private String SECRET_KEY;
|
|
|
+ @Value("${ufop.minio.bucket-name}")
|
|
|
+ private String BUCKET_NAME;
|
|
|
+ @Value("${ufop.minio.FFMPEG_PATH}")
|
|
|
+ private String FFMPEG_PATH;
|
|
|
+
|
|
|
+
|
|
|
+ public static void main(String[] args) throws Exception {
|
|
|
+ // 示例:转换input.mp4到多种格式
|
|
|
+// convertAndUpload("upload/20250611/4d22cdad68fac9a45c8e647e5bb02cc2.mp4", "output", new String[]{"avi"});
|
|
|
+ }
|
|
|
+
|
|
|
+ public void convertAndUpload(String inputKey, String outputPrefix, String[] formats) throws Exception {
|
|
|
+ MinioClient minioClient = MinioClient.builder()
|
|
|
+ .endpoint(MINIO_ENDPOINT)
|
|
|
+ .credentials(ACCESS_KEY, SECRET_KEY)
|
|
|
+ .build();
|
|
|
+
|
|
|
+ // 创建临时目录
|
|
|
+ File tempDir = new File("temp");
|
|
|
+ if (!tempDir.exists()) {
|
|
|
+ tempDir.mkdir();
|
|
|
+ }
|
|
|
+ List<File> filesToDelete = new ArrayList<>(); // 待删除文件列表
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 下载源文件
|
|
|
+ File localInput = new File(tempDir, inputKey);
|
|
|
+ downloadFromMinio(minioClient, BUCKET_NAME, inputKey, localInput);
|
|
|
+ filesToDelete.add(localInput); // 添加到删除列表
|
|
|
+ // 执行格式转换
|
|
|
+ for (String format : formats) {
|
|
|
+ File outputFile = new File(tempDir, outputPrefix + "." + format);
|
|
|
+ convertVideo(localInput.getAbsolutePath(), outputFile.getAbsolutePath(), format);
|
|
|
+
|
|
|
+ // 上传转换后的文件
|
|
|
+ uploadToMinio(minioClient, BUCKET_NAME, outputFile.getName(), outputFile);
|
|
|
+ filesToDelete.add(outputFile); // 添加到删除列表
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ // 清理临时文件(保留目录结构)
|
|
|
+ for (File file : filesToDelete) {
|
|
|
+ if (file.exists() && !file.delete()) {
|
|
|
+ System.err.println("警告:无法删除临时文件 " + file.getAbsolutePath());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+// private void convertVideo(String inputPath, String outputPath, String format) throws IOException, InterruptedException {
|
|
|
+// List<String> command = new ArrayList<>();
|
|
|
+// command.add(FFMPEG_PATH);
|
|
|
+// command.add("-i");
|
|
|
+// command.add(inputPath);
|
|
|
+// command.add("-c:v");
|
|
|
+// command.add("libx264");
|
|
|
+// command.add("-c:a");
|
|
|
+// command.add("aac");
|
|
|
+// command.add(outputPath);
|
|
|
+//
|
|
|
+// ProcessBuilder pb = new ProcessBuilder(command);
|
|
|
+// pb.redirectErrorStream(true);
|
|
|
+// Process process = pb.start();
|
|
|
+//
|
|
|
+// // 读取输出日志
|
|
|
+// try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
|
|
+// String line;
|
|
|
+// while ((line = reader.readLine()) != null) {
|
|
|
+// System.out.println(line);
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// int exitCode = process.waitFor();
|
|
|
+// if (exitCode != 0) {
|
|
|
+// throw new RuntimeException("FFmpeg转换失败,退出码:" + exitCode);
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+ // 修改convertVideo方法以接受文件路径参数
|
|
|
+ private void convertVideo(String inputPath, String outputPath, String format)
|
|
|
+ throws IOException, InterruptedException {
|
|
|
+
|
|
|
+ List<String> command = new ArrayList<>();
|
|
|
+ command.add(FFMPEG_PATH);
|
|
|
+ command.add("-y"); // 覆盖输出文件(避免重复转换问题)
|
|
|
+ command.add("-i");
|
|
|
+ command.add(inputPath);
|
|
|
+ command.add("-c:v");
|
|
|
+ command.add("libx264");
|
|
|
+ command.add("-c:a");
|
|
|
+ command.add("aac");
|
|
|
+ command.add(outputPath);
|
|
|
+
|
|
|
+ ProcessBuilder pb = new ProcessBuilder(command);
|
|
|
+ pb.redirectErrorStream(true);
|
|
|
+ Process process = pb.start();
|
|
|
+
|
|
|
+ // 读取输出日志
|
|
|
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
|
|
+ String line;
|
|
|
+ while ((line = reader.readLine()) != null) {
|
|
|
+ System.out.println(line);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ int exitCode = process.waitFor();
|
|
|
+ if (exitCode != 0) {
|
|
|
+ throw new RuntimeException("FFmpeg转换失败,退出码:" + exitCode);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void downloadFromMinio(MinioClient client, String bucket, String object, File dest) throws Exception {
|
|
|
+ if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucket).build())) {
|
|
|
+ client.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
|
|
|
+ }
|
|
|
+ client.downloadObject(
|
|
|
+ DownloadObjectArgs.builder()
|
|
|
+ .bucket(bucket)
|
|
|
+ .object(object)
|
|
|
+ .filename(dest.getAbsolutePath())
|
|
|
+ .build()
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void uploadToMinio(MinioClient client, String bucket, String object, File file) throws Exception {
|
|
|
+ client.uploadObject(
|
|
|
+ UploadObjectArgs.builder()
|
|
|
+ .bucket(bucket)
|
|
|
+ .object(object)
|
|
|
+ .filename(file.getAbsolutePath())
|
|
|
+ .contentType(getMimeType(object))
|
|
|
+ .build()
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String getMimeType(String fileName) {
|
|
|
+ return switch (fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase()) {
|
|
|
+ case "mp4" -> "video/mp4";
|
|
|
+ case "wmv" -> "video/x-ms-wmv";
|
|
|
+ case "avi" -> "video/avi";
|
|
|
+ case "flv" -> "video/x-flv";
|
|
|
+ case "mpeg" -> "video/mpeg";
|
|
|
+ case "mpg" -> "video/mpeg";
|
|
|
+ case "rmvb" -> "application/vnd.rn-realmedia-vbr";
|
|
|
+ case "mov" -> "video/quicktime";
|
|
|
+ default -> "application/octet-stream";
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void deleteDirectory(File dir) {
|
|
|
+ File[] files = dir.listFiles();
|
|
|
+ if (files != null) {
|
|
|
+ for (File f : files) {
|
|
|
+ if (f.isDirectory()) {
|
|
|
+ deleteDirectory(f);
|
|
|
+ } else {
|
|
|
+ f.delete();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dir.delete();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取转换后的文件大小
|
|
|
+ */
|
|
|
+ public Long getConverterFileSize(String filePath) {
|
|
|
+ Long size = 0L;
|
|
|
+ // 初始化MinIO客户端(需替换您的配置)
|
|
|
+ MinioClient minioClient = MinioClient.builder()
|
|
|
+ .endpoint(MINIO_ENDPOINT)
|
|
|
+ .credentials(ACCESS_KEY, SECRET_KEY)
|
|
|
+ .build();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 1. 构建查询参数
|
|
|
+ StatObjectArgs statArgs = StatObjectArgs.builder()
|
|
|
+ .bucket(BUCKET_NAME)
|
|
|
+ .object(filePath)
|
|
|
+ .build();
|
|
|
+
|
|
|
+ // 2. 获取元数据
|
|
|
+ StatObjectResponse statObjectResponse = minioClient.statObject(statArgs);
|
|
|
+
|
|
|
+ // 3. 提取文件大小
|
|
|
+ size = statObjectResponse.size();
|
|
|
+ System.out.printf("文件大小: %.2f MB%n", size / (1024.0 * 1024.0));
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+
|
|
|
+ }
|
|
|
+ return size;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+//
|
|
|
+// public static void convertAndUpload(String inputObjectPath, String outputPrefix, String[] formats) throws Exception {
|
|
|
+// MinioClient minioClient = MinioClient.builder()
|
|
|
+// .endpoint(MINIO_ENDPOINT)
|
|
|
+// .credentials(ACCESS_KEY, SECRET_KEY)
|
|
|
+// .build();
|
|
|
+//
|
|
|
+// // 1. 从MinIO获取输入流
|
|
|
+// InputStream minioInputStream = minioClient.getObject(
|
|
|
+// GetObjectArgs.builder()
|
|
|
+// .bucket(BUCKET_NAME)
|
|
|
+// .object(inputObjectPath)
|
|
|
+// .build()
|
|
|
+// );
|
|
|
+//
|
|
|
+// // 2. 执行格式转换
|
|
|
+// for (String format : formats) {
|
|
|
+// // 创建FFmpeg进程
|
|
|
+// ProcessBuilder pb = new ProcessBuilder(FFMPEG_PATH,
|
|
|
+// "-i", "pipe:0", // 从标准输入读取
|
|
|
+// "-c:v", "mpeg4", // 示例编码器配置
|
|
|
+// "-c:a", "pcm_s16le",
|
|
|
+// "-f", format, // 强制指定输出格式
|
|
|
+// "pipe:1" // 输出到标准输出
|
|
|
+// );
|
|
|
+//
|
|
|
+// pb.redirectErrorStream(true);
|
|
|
+// Process ffmpegProcess = pb.start();
|
|
|
+//
|
|
|
+// // 3. 创建上传任务
|
|
|
+// CompletableFuture.runAsync(() -> {
|
|
|
+// try {
|
|
|
+// // 构建MinIO上传参数
|
|
|
+// PutObjectArgs putArgs = PutObjectArgs.builder()
|
|
|
+// .bucket(BUCKET_NAME)
|
|
|
+// .object(getOutputObjectName(inputObjectPath, format))
|
|
|
+// .contentType(getMimeType(format))
|
|
|
+// .build();
|
|
|
+//
|
|
|
+// // 获取可写的OutputStream(正确方式)
|
|
|
+// try () {
|
|
|
+// // 创建管道线程(单向传输:FFmpeg → MinIO)
|
|
|
+// Thread outputThread = new Thread(() -> {
|
|
|
+// try {
|
|
|
+// pipeStreams(
|
|
|
+// ffmpegProcess.getInputStream(),
|
|
|
+// minioOutputStream
|
|
|
+// );
|
|
|
+// } catch (IOException e) {
|
|
|
+// throw new RuntimeException(e);
|
|
|
+// }
|
|
|
+// });
|
|
|
+//
|
|
|
+// outputThread.start();
|
|
|
+// outputThread.join();
|
|
|
+//
|
|
|
+// // 等待FFmpeg进程完成
|
|
|
+// int exitCode = ffmpegProcess.waitFor();
|
|
|
+// if (exitCode != 0) {
|
|
|
+// throw new RuntimeException("FFmpeg转换失败,退出码:" + exitCode);
|
|
|
+// }
|
|
|
+// }
|
|
|
+// } catch (Exception e) {
|
|
|
+// throw new RuntimeException("转换/上传过程出错", e);
|
|
|
+// }
|
|
|
+// }).join();
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// private static void pipeStreams(InputStream src, OutputStream dest) throws IOException {
|
|
|
+// byte[] buffer = new byte[8192];
|
|
|
+// int bytesRead;
|
|
|
+// while ((bytesRead = src.read(buffer)) != -1) {
|
|
|
+// dest.write(buffer, 0, bytesRead);
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// private static String getOutputObjectName(String inputPath, String format) {
|
|
|
+// String baseName = inputPath.substring(0, inputPath.lastIndexOf('.'));
|
|
|
+// return baseName + "_converted." + format;
|
|
|
+// }
|
|
|
+//
|
|
|
+// private static String getMimeType(String format) {
|
|
|
+// String lowerFormat = format.toLowerCase();
|
|
|
+// switch (lowerFormat) {
|
|
|
+// case "mp4":
|
|
|
+// return "video/mp4";
|
|
|
+// case "wmv":
|
|
|
+// return "video/x-ms-wmv";
|
|
|
+// case "avi":
|
|
|
+// return "video/x-msvideo";
|
|
|
+// case "flv":
|
|
|
+// return "video/x-flv";
|
|
|
+// case "mpeg":
|
|
|
+// case "mpg":
|
|
|
+// return "video/mpeg";
|
|
|
+// case "rmvb":
|
|
|
+// return "application/vnd.rn-realmedia-vbr";
|
|
|
+// case "mov":
|
|
|
+// return "video/quicktime";
|
|
|
+// default:
|
|
|
+// return "application/octet-stream";
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+}
|