JAVE实现音频截取并上传OSS

2022-07-28,,,,

引言

前段时间有个项目需求,需要对一段完整的音频按照开始时间和结束时间进行截取。网上查了一些相关资料,最后采用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

《JAVE实现音频截取并上传OSS.doc》

下载本文的Word格式文档,以方便收藏与打印。