Spring boot 整合 ffmpeg 实现给视频添加文字水印 只有上传minio(理论通用!!)
只要有ffmpeg 命令理论可以实现所有ffmpeg 能做的的事儿!!
思路:
前端上传视频通过commons-io的FileUtils.copyInputStreamToFile()将流复制到文件中 java执行ffmpeg 命令对这个文件进行转换输出到另外一个临时文件 在将添加水印后的文件转成inputStream流上传到minio(本人小白 可能有更好的思路!!!)
上代码:
首先是minio的配置文件不需要的掠过
application.yml
spring:
servlet:
multipart:
max-file-size: 200MB
max-request-size: 200MB
minio:
endpoint: http://192.168.22.129:9090 #minio地址 注意一定要加 协议不然报错
accessKey: admin # 账号
secretKey: admin123456 #密码
bucketName: text # 桶的名字(可以理解为文件夹目录)
MinIoClientConfig:
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Data
@Component
public class MinIoClientConfig {
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey}")
private String secretKey;
/**
* 注入minio 客户端
* @return
*/
@Bean
public MinioClient minioClient(){
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
}
MinioUtilS:注意里面包含了 视频添加文字水印的方法 等下我也会贴出来!!!
import io.minio.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.apache.commons.io.FileUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @description: minio工具类
* @version:3.0
*/
@Component
public class MinioUtilS {
@Autowired
private MinioClient minioClient;
@Value("${minio.bucketName}")
private String bucketName;
/**
* description: 判断bucket是否存在,不存在则创建
*
* @return: void
*/
public void existBucket(String name) {
try {
boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
if (!exists) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建存储bucket
* @param bucketName 存储bucket名称
* @return Boolean
*/
public Boolean makeBucket(String bucketName) {
try {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 删除存储bucket
* @param bucketName 存储bucket名称
* @return Boolean
*/
public Boolean removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* description: 上传文件
*
* @param multipartFile
* @return: java.lang.String
*/
public List<String> upload(MultipartFile[] multipartFile) {
List<String> names = new ArrayList<>(multipartFile.length);
for (MultipartFile file : multipartFile) {
String fileName = file.getOriginalFilename();
String[] split = fileName.split("\\.");
if (split.length > 1) {
fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1];
} else {
fileName = fileName + System.currentTimeMillis();
}
InputStream in = null;
System.err.println("文件名是:"+fileName);
try {
in = file.getInputStream();
// InputStream stream = addWatermark(file.getInputStream(),fileName);
File videoFile = File.createTempFile(split[0],"."+split[1]);
FileUtils.copyInputStreamToFile(in, videoFile);
String text = "Hello, World!";
System.err.println("路径"+videoFile.getAbsolutePath());
String newFile = UUID.randomUUID().toString()+"."+split[1];
addTextWatermark(videoFile.getAbsolutePath(),text, newFile);
File output = new File(newFile);
InputStream inputStream = new FileInputStream(output);
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(inputStream, inputStream.available(), -1)
.contentType(file.getContentType())
.build()
);
inputStream.close();
output.delete();
videoFile.delete();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
names.add(fileName);
}
return names;
}
/**
* 视频添加文字水印
* @param videoFilePath
* @param text
* @param outputFilePath
* @throws IOException
* @throws InterruptedException
*/
public static void addTextWatermark(String videoFilePath, String text, String outputFilePath) throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("ffmpeg", "-i", videoFilePath, "-vf", "drawtext=text='智慧学堂':fontfile=font.ttf:fontsize=24:fontcolor=black@1.0:x=10:y=10", outputFilePath);
pb.redirectErrorStream(true);
pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
pb.environment().put("LANG", "zh_CN.GBK");
try {
Process process = pb.start();
InputStream is = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
process.waitFor();
System.out.println("FFmpeg command completed.");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
/**
* description: 下载文件
*
* @param fileName
* @return: org.springframework.http.ResponseEntity<byte [ ]>
*/
public ResponseEntity<byte[]> download(String fileName) {
ResponseEntity<byte[]> responseEntity = null;
InputStream in = null;
ByteArrayOutputStream out = null;
try {
in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
out = new ByteArrayOutputStream();
IOUtils.copy(in, out);
//封装返回值
byte[] bytes = out.toByteArray();
HttpHeaders headers = new HttpHeaders();
try {
headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
headers.setContentLength(bytes.length);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setAccessControlExposeHeaders(Arrays.asList("*"));
responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return responseEntity;
}
/**
* 查看文件对象
* @param bucketName 存储bucket名称
* @return 存储bucket内文件对象信息
*/
public List<ObjectItem> listObjects(String bucketName) {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).build());
List<ObjectItem> objectItems = new ArrayList<>();
try {
for (Result<Item> result : results) {
Item item = result.get();
ObjectItem objectItem = new ObjectItem();
objectItem.setObjectName(item.objectName());
objectItem.setSize(item.size());
objectItems.add(objectItem);
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return objectItems;
}
/**
* 批量删除文件对象
* @param bucketName 存储bucket名称
* @param objects 对象名称集合
*/
public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());
Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
return results;
}
}
上传文件到minio的方法:
/**
* description: 上传文件
*
* @param multipartFile
* @return: java.lang.String
*/
public List<String> upload(MultipartFile[] multipartFile) {
List<String> names = new ArrayList<>(multipartFile.length);
for (MultipartFile file : multipartFile) {
String fileName = file.getOriginalFilename();
String[] split = fileName.split("\\.");
if (split.length > 1) {
fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1];
} else {
fileName = fileName + System.currentTimeMillis();
}
InputStream in = null;
//文件名简单的处理了下 你们可以自己处理
System.err.println("文件名是:"+fileName);
try {
in = file.getInputStream();
//定义临时文件 吧前端传入的流转成临时文件
File videoFile = File.createTempFile(split[0],"."+split[1]);
FileUtils.copyInputStreamToFile(in, videoFile);
//定义文字水印
String text = "Hello, World!";
//定义输出文件(添加完水印的视频)的名称以及后缀
String newFile = UUID.randomUUID().toString()+"."+split[1];
//调用添加水印的方法 传入
addTextWatermark(videoFile.getAbsolutePath(),text, newFile);
// 读取添加完成的文件转成 InputStream
File output = new File(newFile);
InputStream inputStream = new FileInputStream(output);
// 上传到minio
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(inputStream, inputStream.available(), -1)
.contentType(file.getContentType())
.build()
);
//因为作用域的原因我在这里进行关闭流 不然临时文件我发删除
inputStream.close();
//删除临时文件
output.delete();
videoFile.delete();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
names.add(fileName);
}
return names;
}
视频添加文字水印的方法:
/**
* 视频添加文字水印
* @param videoFilePath
* @param text
* @param outputFilePath
* @throws IOException
* @throws InterruptedException
*/
public static void addTextWatermark(String videoFilePath, String text, String outputFilePath) throws IOException, InterruptedException {
// 理论修改命令可以完成更多操作 我这里的文字水印写死了 你们改下
ProcessBuilder pb = new ProcessBuilder("ffmpeg", "-i", videoFilePath, "-vf", "drawtext=text='测试一下':fontfile=font.ttf:fontsize=24:fontcolor=black@1.0:x=10:y=10", outputFilePath);
pb.redirectErrorStream(true);
pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
// 处理中文水印乱码的问题
pb.environment().put("LANG", "zh_CN.GBK");
try {
Process process = pb.start();
InputStream is = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
process.waitFor();
System.out.println("FFmpeg command completed.");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}