欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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