JAVE实现音频截取并上传OSS
程序员文章站
2022-03-26 16:44:43
引言前段时间有个项目需求,需要对一段完整的音频按照开始时间和结束时间进行截取。网上查了一些相关资料,最后采用JAVE Java类库实现。介绍JAVE (Java Audio Video Encoder) 类库是一个 ffmpeg 项目的 Java 语言封装。开发人员可以使用JAVE 在不同的格式间转换视频和音频,实现视频和音频文件的截取功能。思路1. 如果是直接截取本地文件,代码很简单,直接引入jave jar包,实现截取功能即可。2. 对于不是本地文件,而是一个远程文件的链接情况,就需要先下载...
引言
前段时间有个项目需求,需要对一段完整的音频按照开始时间和结束时间进行截取。网上查了一些相关资料,最后采用JAVE Java类库实现。
介绍
JAVE (Java Audio Video Encoder) 类库是一个 ffmpeg 项目的 Java 语言封装。开发人员可以使用JAVE 在不同的格式间转换视频和音频,实现视频和音频文件的截取功能。
思路
1. 如果是直接截取本地文件,代码很简单,直接引入jave jar包,实现截取功能即可。
2. 对于不是本地文件,而是一个远程文件的链接情况,就需要先下载文件到指定路径,再进行截取,最后将截取后的路径更新到对应记录,便可以拿到一个直接可下载的链接。在项目中,使用的是oss进行文件管理,所以又涉及到一些oss的使用。
实现及核心代码
1. 下载jar包,并上传到maven仓库
jave jar包下载地址
参考博客:jar上传Maven私服
2. 实现音频截取代码
将jar上传到私服后,就可以直接在maven项目中引入依赖,下面是截取音频代码:
package com.quyixian.word.book.back.utils;
import it.sauronsoftware.jave.Encoder;
import it.sauronsoftware.jave.MultimediaInfo;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* 截取音频文件工具类
*/
@Slf4j
public class ReadVideoUtil {
/**
* 截取mp3音频文件
*
* @param sourceFile 源文件地址
* @param targetFile 目标文件地址
* @param start 截取开始时间(秒)
* @param end 截取结束时间(秒)
* <p>
* return 截取成功返回true,否则返回false
*/
public static boolean cut(String sourceFile, String targetFile, int start, int end) {
try {
if (!sourceFile.toLowerCase().endsWith(".mp3") || !targetFile.toLowerCase().endsWith(".mp3")) {
return false;
}
File wav = new File(sourceFile);
if (!wav.exists()) {
return false;
}
//总时长(秒)
long t1 = getTimeLen(wav);
if (start < 0 || end <= 0 || end > t1 || start >= end) {
return false;
}
FileInputStream fis = new FileInputStream(wav);
//音频数据大小(44为128kbps比特率wav文件头长度)
long wavSize = wav.length() - 44;
//截取的音频数据大小
long splitSize = (wavSize / t1) * (end - start);
//截取时跳过的音频数据大小
long skipSize = (wavSize / t1) * start;
int splitSizeInt = Integer.parseInt(String.valueOf(splitSize));
int skipSizeInt = Integer.parseInt(String.valueOf(skipSize));
//存放文件大小,4代表一个int占用字节数
ByteBuffer buf1 = ByteBuffer.allocate(4);
//放入文件长度信息
buf1.putInt(splitSizeInt + 36);
//代表文件长度
byte[] flen = buf1.array();
//存放音频数据大小,4代表一个int占用字节数
ByteBuffer buf2 = ByteBuffer.allocate(4);
//放入数据长度信息
buf2.putInt(splitSizeInt);
//代表数据长度
byte[] dlen = buf2.array();
//数组反转
flen = reverse(flen);
dlen = reverse(dlen);
//定义wav头部信息数组
byte[] head = new byte[44];
//读取源wav文件头部信息
fis.read(head, 0, head.length);
//4代表一个int占用字节数
for (int i = 0; i < 4; i++) {
//替换原头部信息里的文件长度
head[i + 4] = flen[i];
//替换原头部信息里的数据长度
head[i + 40] = dlen[i];
}
//存放截取的音频数据
byte[] fbyte = new byte[splitSizeInt + head.length];
//放入修改后的头部信息
for (int i = 0; i < head.length; i++) {
fbyte[i] = head[i];
}
//存放截取时跳过的音频数据
byte[] skipBytes = new byte[skipSizeInt];
//跳过不需要截取的数据
fis.read(skipBytes, 0, skipBytes.length);
//读取要截取的数据到目标数组
fis.read(fbyte, head.length, fbyte.length - head.length);
fis.close();
File target = new File(targetFile);
//如果目标文件已存在,则删除目标文件
if (target.exists()) {
target.delete();
}
FileOutputStream fos = new FileOutputStream(target);
fos.write(fbyte);
fos.flush();
fos.close();
} catch (IOException e) {
log.error("[ReadVideoUtil.cut] [error] [sourceFile is {}, targetFile is{}, start time is {}, end time is {}]", sourceFile, targetFile, start, end, e);
return false;
}
return true;
}
/**
* 获取音频文件总时长
*
* @param filePath
* @return
*/
public static long getTimeLen(File filePath) {
long tlen = 0;
if (filePath != null && filePath.exists()) {
Encoder encoder = new Encoder();
try {
MultimediaInfo m = encoder.getInfo(filePath);
long ls = m.getDuration();
tlen = ls / 1000;
} catch (Exception e) {
log.error("[ReadVideoUtil.getTimeLen] [error] [param is {}]", filePath, e);
}
}
return tlen;
}
/**
* 数组反转
*
* @param array
*/
public static byte[] reverse(byte[] array) {
byte temp;
int len = array.length;
for (int i = 0; i < len / 2; i++) {
temp = array[i];
array[i] = array[len - 1 - i];
array[len - 1 - i] = temp;
}
return array;
}
/**
* 使用本地文件测试截取效果
*
* @param args
*/
public static void main(String[] args) {
System.out.println(cut("E:\\7a0f34cb-e9e2-4863-b267-140d3f915929.mp3", "E:\\7a0f34cb-e9e2-4863-b267-140d3f915929-cut_8_30.mp3", 8, 30));
}
}
因为提供给我们的音频文件格式是mp3,所以上面代码检查了文件是否是mp3,其他格式的音频如wav也是可以实现功能的。
以上代码来源博客:Java切割音频文件
3. 根据URL下载文件到指定路径
/**
* 文件下载
*
* @param audioPath
* @param savePath 文件地址 示例:D:/ceshi/1.png
* @throws Exception
*/
public static void downloadFile(String audioPath, String savePath) throws Exception {
DataInputStream in = null;
DataOutputStream out = null;
try {
File file = new File(savePath);
if (!file.exists()) {
//判断文件是否存在,不存在则创建文件
file.createNewFile();
}
URL url = new URL(audioPath);
HttpURLConnection urlCon = (HttpURLConnection) url.openConnection();
urlCon.setConnectTimeout(6000);
urlCon.setReadTimeout(6000);
int code = urlCon.getResponseCode();
if (code != HttpURLConnection.HTTP_OK) {
throw new Exception("文件读取失败");
}
in = new DataInputStream(urlCon.getInputStream());
out = new DataOutputStream(new FileOutputStream(savePath));
byte[] buffer = new byte[2048];
int count = 0;
while ((count = in.read(buffer)) > 0) {
out.write(buffer, 0, count);
}
} catch (Exception e) {
log.error("save file", e);
} finally {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
}
}
4. 上传至阿里云OSS
(1)引入aliyun-oss-sdk
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.8.3</version>
</dependency>
(2)配置密钥等信息
oss:
access-key-id: ******
access-key-secret: ******
end-point: ******
bucket-name: ******
(3)上传文件至OSS
@Resource
private OssConfig ossConfig;
private OSSClient ossClient;
/**
* 上传文件
*
* @param url 源文件路径
* @param fileDir 上传到OSS的位置
* @return
*/
public String upload(String url, String fileDir) {
try {
File file = new File(url);
FileInputStream fileInputStream = new FileInputStream(file);
String[] split = url.split("/");
String result = this.uploadFileOSS(fileInputStream, split[split.length - 1], fileDir);
if (!StringUtil.isEmpty(result)) {
// 上传oss成功之后删除临时文件
file.delete();
return result;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
log.error("上传文件失败");
}
return "";
}
/**
* 上传到OSS服务器 如果同名文件会覆盖
*
* @param inputStream 文件流
* @param fileName 文件名称 包括后缀名
* @return 出错返回"" ,唯一MD5数字签名
*/
public String uploadFileOSS(InputStream inputStream, String fileName, String fileDir) {
long currentTime = System.currentTimeMillis();
String ret = "";
try {
ossClient = new OSSClient(ossConfig.getEndPoint(), ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret());
// 创建上传Object的Metadata
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(inputStream.available());
objectMetadata.setCacheControl("no-cache");
objectMetadata.setHeader("Pragma", "no-cache");
objectMetadata.setContentType(getcontentType(fileName.substring(fileName.lastIndexOf("."))));
objectMetadata.setContentDisposition("inline;filename=" + fileName);
log.info("开始上传文件...");
PutObjectResult putResult = ossClient.putObject(ossConfig.getBucketName(), fileDir + fileName, inputStream, objectMetadata);
if (StringUtils.isNotBlank(putResult.getETag())) {
ret = fileDir + fileName;
}
log.info("文件上传完成,耗时:{}ms", System.currentTimeMillis() - currentTime);
} catch (IOException e) {
log.error("上传文件到OSS时出现未知异常:" + e);
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
log.error("上传文件到OSS后,关闭IO流时出现未知异常:" + e);
}
}
return ret;
}
/**
* Description: 判断OSS服务文件上传时文件的contentType
* <p>
* 文件后缀
*
* @return String
*/
public static String getcontentType(String filenameExtension) {
if (filenameExtension.equalsIgnoreCase("bmp")) {
return "image/bmp";
}
if (filenameExtension.equalsIgnoreCase("gif")) {
return "image/gif";
}
if (filenameExtension.equalsIgnoreCase("jpeg") || filenameExtension.equalsIgnoreCase("jpg")
|| filenameExtension.equalsIgnoreCase("png")) {
return "image/jpeg";
}
if (filenameExtension.equalsIgnoreCase("html")) {
return "text/html";
}
if (filenameExtension.equalsIgnoreCase("txt")) {
return "text/plain";
}
if (filenameExtension.equalsIgnoreCase("vsd")) {
return "application/vnd.visio";
}
if (filenameExtension.equalsIgnoreCase("pptx") || filenameExtension.equalsIgnoreCase("ppt")) {
return "application/vnd.ms-powerpoint";
}
if (filenameExtension.equalsIgnoreCase("docx") || filenameExtension.equalsIgnoreCase("doc")) {
return "application/msword";
}
if (filenameExtension.equalsIgnoreCase("pdf")) {
return "application/pdf";
}
if (filenameExtension.equalsIgnoreCase("xml")) {
return "text/xml";
}
if (filenameExtension.equalsIgnoreCase(".mp3")) {
return "audio/mp3";
}
if (filenameExtension.toLowerCase().endsWith("mp4")) {
return "video/mpeg4";
}
if (filenameExtension.toLowerCase().endsWith("wav")) {
return "audio/wav";
}
return "image/jpeg";
}
总结
遇到这种没有接触过的需求,首先要做的是通过查询资料确定实现方案。确定方案之后可以先自己实现demo,再结合项目实际场景,完成需求就很容易了。
本文地址:https://blog.csdn.net/u013034223/article/details/109642127
下一篇: 螃蟹不能和什么一起吃 吃螃蟹的禁忌