视频加密并上传至oss实现高并发访问

前言

本篇文章主要是分享代码,代码实现的功能是从oss上指定bucket下的upload目录下载视频到本地,然后通过openssl生成key与iv和keyinfo文件,然后调用脚本通过ffmpeg指令切分mp4视频到当前目录下,由于是调用脚本所以程序不会等待视频切分完成,会立马往下执行,这时通过获取视频时长计算视频ts切片数量,然后统计目录下ts文件数量,一旦大于或者等于立马停止等待,遍历目录下的ts、m3u8与key文件进行上传到oss中。使用m3u8的原始是为了视频安全,使用oss是为了实现视频的高并发访问。记录一下在生成环境遇到的视频加密问题,不是很清楚加密思路的可以先看lz的这篇文章,了解加密用到的相关技术:Java实现视频加密及播放 之前我是在windows上测试视频切分功能问题不大,但是到了Linux环境发现了很多问题,例如无法通过ProcessBuild(类似于黑窗口)执行语句,主要是openssl的语句通过黑窗口执行会报错,具体原因还不是很清楚,所以通过调用脚本的方式达到目的。另外还有一个Linux环境默认安装的ffmpeg无法进行视频加密操作,需要安装一系列的依赖,安装方式可以参考下面的方式,我用过其他方式安装无法进行视频截取,生成key相关的资料可以参数这个:使用ffmpeg视频切片并加密。另外传到oss上的视频文件需要转成汉语拼音,否则播放的时候会出现Live(直播)以及无法拖动进度条的问题

另外想跟大家说的是如果项目中还没有整合oss的,可以将视频存储至nginx或者tomcat下,只要你看懂了实现思路,那肯定是比上传至oss更加简单的。


安装ffmpeg

yum -y install epel-release
rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-1.el7.nux.noarch.rpm
yum -y install ffmpeg ffmpeg-devel
ffmpeg -version

使用指令测试切分情况

ffmpeg -i 22.mp4 -profile:v baseline -level 3.0  -start_number 0 -hls_time 10 -hls_list_size 0 -f hls index.m3u8

22.mp4代表要切分视频名称,hls_time 代表每个ts的时长,index.m3u8代表输出的文件路径和名称


代码

本代码只是用Linux系统,请不到在windows上测试,代码写好后打包到Linux上测试
本代码只是用Linux系统,请不到在windows上测试,代码写好后打包到Linux上测试
本代码只是用Linux系统,请不到在windows上测试,代码写好后打包到Linux上测试

相关依赖

        <!--阿里云OSS依赖-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.8.0</version>
        </dependency>
        
   		<!-- IOUtils.toString()方法用到了-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.3</version>
        </dependency>
        
        <!-- 拼音工具类库,代码中没有用过,我这里是前端实现的,用来避免上传到oss
        中出错 -->
        <dependency>
            <groupId>com.belerweb</groupId>
            <artifactId>pinyin4j</artifactId>
            <version>2.5.1</version>
        </dependency>

		

代码实现的效果是从oss上下载视频到本地,做视频切分和加密后重传到oss中,要求oss中的bucket权限为:公开读-公开读-公开读

涉及的方法比较多,由于这是项目的代码不方便直接复制类分享,所以只能分享方法,有些方法可能在不同的类中,可以自己调整用到的类应该都分享出来了。另外lz建议先将所有的方法放到一个类中,待接口调通后放到工具类方法中,再将不同的方法防到例如FileUtil、M3u8Util、OSSUtil等工具类中

测试接口方法

    //用户存储加密切分后的视频bucket
    public static final String ENCRYPT_BUCKET_NAME = "YourBucketName";
    //生成iv文件的名称
    public static final String IV_NAME = "iv.txt";
    //keyinfo的文件名称
    public static final String ENC_KEY_INFO = "enc.keyinfo";
    //生成的加密文件名称
    public static final String ENC_KEY = "enc.key";
/**
     * 测试将oss的视频转化成m3u8并存储到本地
     * 三个参数含义:bucket名称,视频所处的路径,视频名称
     * 要求bucket的upload下必须有叫做a.mp4的视频文件
     *
     * @return
     */
    @ResponseBody
    @GetMapping("/mp4Tom3u8AndEncrypt")
    public ResponseResult mp4Tom3u8AndEncrypt() {
        mp4Tom3u8AndEncrypt("YourBacketName", "upload/", "a.mp4");
        return ResponseResult.success();
    }

oss中的文件
在这里插入图片描述
mp4Tom3u8AndEncrypt方法

  • endpoint, accessKeyId, accessKeySecret使用自己的不知道怎么获取的可以查看阿里云文档
/**
     * 将oss中的视频下载到本地,然后进行加密切分成m3u8文件
     * 生成key文件参考资料:https://www.cnblogs.com/codeAB/p/9184266.html
     *
     * @param bucketName 文件所处在的oss的bucket名称
     * @param path       文件所在的路径,例如:upload/
     * @param name       文件名称,例如:8KAnimal.mp4
     * @return 返回所有上传的文件路径集合
     */
    public static List<String> mp4Tom3u8AndEncrypt(String bucketName, String path, String name) {
        //文件在oss中的路径加名称
        String objectName = path + name;
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        //判断当前oss上文件是否存在
        boolean found = ossClient.doesObjectExist(bucketName, objectName);
        if (!found) {
            throw new BusinessException(CommonExEnum.OSS_FILE_NOT_EXIST);
        }
        //将oss文件下载到本地服务器,返回值路径+文件名称
        String storagePath = downloadToLocal(bucketName, path, name);
        //截取文件暂存路径
        String storageFilePath = storagePath.substring(0, storagePath.indexOf(name));
        log.info(storageFilePath);
        //生成16位enc.key
        String encKey = generateRandom(16);
        log.info(encKey);
        //创建enc.key并将上面生成的encKey写入到文件中
        createFileWriterContent(storageFilePath, ENC_KEY, encKey);
        //生成vi文件
        generateIVFile(storageFilePath);
        //生成enc.keyinfo文件,enc.key的ng路径地址,ng本地地址,iv文件内容
        String content = ENC_KEY + "\n" +
                storageFilePath + File.separator + ENC_KEY + "\n" +
        readFileContent(storageFilePath + File.separator + IV_NAME);
        log.info("***keyinfo文件的内容为:{}***", content);
        createFileWriterContent(storageFilePath, ENC_KEY_INFO,
                content);

        //使用ffmpeg指令调用服务器/data/shell目录下的脚本实现视频切分和加密
        dealMp4ToM3u8AndEnc(storageFilePath + File.separator + ENC_KEY_INFO,
                storageFilePath + File.separator + name);

        //由于ffmpeg视频切分是调用服务器脚本,代码执行与脚本执行是异步操作,
        // 所以需要等待一段时间待视频切分完成后将文件上传到oss中
        int duration = calculateVideoDuration(storageFilePath + File.separator + name);
        log.info("***视频时长为:【{}】秒***", duration);

        //计算ts的数量,向上取整。20是每个ts文件的视频长度,每个是20秒
        int fragmentNum = (int) Math.ceil((float) duration / 20);
        log.info("***ts数量为:{}***", fragmentNum);

        //等待视频切割完成,通过检测文件夹下总ts文件数量与计算的ts数量是否相同判断切割是否完成
        // (切割后端ts总大小与原视频大小不一致)
        waitVideoSegmentFinish(storageFilePath, fragmentNum);

        //获取文件路径下所有ts与m3u8、enc.key文件列表
        List<String> uploadPathList = getUploadPathList(storageFilePath);

		//获取文件名称
        String filePrefix = name.substring(0, name.indexOf("."));


        //将文件上传到oss上
        for (String uploadFile : uploadPathList) {
            File file = new File(uploadFile);
            String uploadFileName = file.getName();
            //创建oss上传的路径,加密后的文件传到security下,未加密的传到upload目录下
            String fileName = "security/" + filePrefix + "/" + uploadFileName;

            try {
                String s = uploadFileWithInputSteam(ENCRYPT_BUCKET_NAME, fileName,
                        new FileInputStream(file));
                //上传视频文件为中文名时,oss会返回URL编码后的路径,所以要将返回回来的路径进行URL解码后再存入数据库
                s = URLDecoder.decode(s, "utf-8");
                log.info("***文件上传后的绝对路径为:【{}】***", s);
                ossPathList.add(s);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        return "";
    }

下面的方法是mp4Tom3u8AndEncrypt中用到的方法

downloadToLocal方法
注意

  • 替换endpoint, accessKeyId, accessKeySecret参数
  • /opt/nginx/html/m3u8/ 字符串换成自己服务器上nginx的html目录并且nginx.conf文件需要做配置,默认不支持播放m3u8文件,配置防止可以在Java实现视频加密及播放
/**
     * 下载到本地临时文件夹
     *
     * @param bucketName 存储名称
     * @param path       文件夹路径,例如:abc/efg/
     * @param name       文件名称,例如:123.jpg
     * @return filename 下载后的文件全路径名
     */
    public static String downloadToLocal(String bucketName, String path, String name) {
        String objectName = path + name;
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        String tempPath;
        String osName = System.getProperty("os.name");
        String fileName = name.substring(0, name.indexOf("."));
        if (osName.startsWith("Windows")) {
            tempPath = "C:\\Users\\Administrator\\Desktop\\m3u8\\codeEncrypt\\" + fileName;
        } else {
            //用于存储oss上下载下来的临时存储文件,必须是nginx的html路径,由于生成keyinfo文件需要路径参数
            tempPath = "/opt/nginx/html/m3u8/" + fileName;
        }
        File uploadFile = new File(tempPath);
        if (!uploadFile.exists()) {
            log.info("创建的文件夹目录:{}", uploadFile.getPath());
            uploadFile.mkdirs();
        }
        // 下载OSS文件到本地文件。如果指定的本地文件存在会覆盖,不存在则新建。
        ossClient.getObject(new GetObjectRequest(bucketName, objectName),
                new File(tempPath + File.separator + name));

        // 关闭OSSClient。
        ossClient.shutdown();
        return tempPath + name;
    }

readFileContent方法

/**
     * 读取文件内容并返回
     *
     * @param fileName 文件完整路径
     * @return 文件内容
     */
    public static String readFileContent(String fileName) {
        File file = new File(fileName);
        BufferedReader reader = null;
        StringBuilder sbf = new StringBuilder();
        try {
            reader = new BufferedReader(new FileReader(file));
            String tempStr;
            while ((tempStr = reader.readLine()) != null) {
                sbf.append(tempStr);
            }
            reader.close();
            return sbf.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
        return sbf.toString();
    }

generateRandom方法
生成一定长度的字母与数字随机数

/**
     * 生成一定长度的字母与数字随机数
     *
     * @param length 长度
     * @return 随机数
     */
    public static String generateRandom(int length) {
        StringBuilder val = new StringBuilder();
        Random random = new Random();
        //参数length,表示生成几位随机数
        for (int i = 0; i < length; i++) {
            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
            //输出字母还是数字
            if ("char".equalsIgnoreCase(charOrNum)) {
                //输出是大写字母还是小写字母
                int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
                val.append((char) (random.nextInt(26) + temp));
            } else if ("num".equalsIgnoreCase(charOrNum)) {
                val.append(String.valueOf(random.nextInt(10)));
            }
        }
        return val.toString();
    }

createFileWriterContent方法

/**
     * 创建文件并写入内容
     *
     * @param filePath 文件路径
     * @param fileName 文件名称
     * @param content  需要写入的文件内容
     * @throws IOException
     */
    public static void createFileWriterContent(String filePath, String fileName, String content) {
        File dir = new File(filePath);
        // 一、检查放置文件的文件夹路径是否存在,不存在则创建
        if (!dir.exists()) {
            dir.mkdirs();// mkdirs创建多级目录
        }
        File checkFile = new File(filePath + File.separator + fileName);
        FileWriter writer = null;
        try {
            // 二、检查目标文件是否存在,不存在则创建
            if (!checkFile.exists()) {
                checkFile.createNewFile();// 创建目标文件
            }
            // 三、向目标文件中写入内容
            // FileWriter(File file, boolean append),append为true时为追加模式,false或缺省则为覆盖模式
            writer = new FileWriter(checkFile);
            writer.append(content);
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != writer) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

generateIVFile方法
注意

  • 这个方法会运行服务器中/data/shell的脚本,目录下有两个脚本,在这个方法下直接复制出来
/**
     * 生成视频播放所需要的的iv文件
     * generate-iv.sh脚本指令:openssl rand -hex 16
     *
     * @param path 生成iv文件的工作路径,不带文件名
     */
    public static void generateIVFile(String path) {
        List<String> command = new ArrayList<>();
        command.add("sh");
        command.add("/data/shell/generate-iv.sh");
        //传入生成iv路径的参数,指定iv文件生成路径
        command.add(path + File.separator);
        System.out.println("command命令:{}" + command.toString());
        ProcessBuilder builder = new ProcessBuilder(command);
        try {
            Process start = builder.start();
            BufferedReader br2 = new BufferedReader(new InputStreamReader(start.getErrorStream()));
            StringBuilder buf = new StringBuilder(); // 保存输出结果流
            String line;
            while ((line = br2.readLine()) != null) buf.append(line);
            System.out.println("执行生成iv文件脚本返回输出结果:{}" + buf.toString());

        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("***生成iv文件完成***");
    }

generate-ts-video.sh脚本,放至/data/shell/目录下
$1、$2、$3代表脚本参数,调用脚本时传入

ffmpeg -i $1 -profile:v baseline -level 3.0 -start_number 0 -hls_time 20 -hls_list_size 0 -f hls -hls_key_info_file $2 $3

generate-iv.sh脚本,防至/data/shell/目录下

openssl rand -hex 16 > $1iv.txt

如图
在这里插入图片描述

dealMp4ToM3u8AndEnc方法

/**
     * 将mp4文件切分成ts文件,并根据keyinfo文件加密
     * generate-ts-video.sh脚本中ffmpeg切分指令:
     * ffmpeg -i a.mp4 -profile:v baseline -level 3.0  -start_number 0
     * -hls_time 20 -hls_list_size 0 -f hls -hls_key_info_file enc.keyinfo index.m3u8
     *
     * @param sourcePath  需要加密的视频路径
     * @param keyInfoPath keyinfo文件的路径
     * @throws IOException
     */
    public static void dealMp4ToM3u8AndEnc(String keyInfoPath,
                                           String sourcePath) {
        List<String> command = new ArrayList<>();
        command.add("sh");
        command.add("/data/shell/generate-ts-video.sh");
        //需要切分的视频路径
        command.add(sourcePath);
        //keyinfo文件位置
        command.add(keyInfoPath);
        //自定义切分名称,同时包含m3u8文件的输出路径
        command.add(sourcePath.replace("mp4", "m3u8"));
        long start = System.currentTimeMillis();
        ProcessBuilder builder = new ProcessBuilder(command);
        try {
            builder.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        log.info(Arrays.toString(builder.command().toArray()));
        log.info("总共耗时=" + (end - start) + "毫秒");
    }

calculateVideoDuration方法

 /**
     * 计算视频的时长
     *
     * @param storageFilePath 视频路径
     * @return 视频时长,单位为秒
     */
    public static int calculateVideoDuration(String storageFilePath) {
        //生成获取视频时长的指令
        String dos = "ffprobe " + storageFilePath;
        //开始时间
        long time = System.currentTimeMillis();
        String dosText = execDos(dos);
        System.out.println("耗时:" + (System.currentTimeMillis() - time) + "毫秒");
        System.out.println("dos文本:" + dosText);
        int startIndex = dosText.indexOf("Duration: ") + "Duration: ".length();
        System.out.println("startIndex:" + startIndex);
        int endIndex = dosText.indexOf(", start", startIndex);
        System.out.println("endIndex:" + endIndex);
        String durationText = dosText.substring(startIndex, endIndex).trim();
        System.out.println("视频时长文本为:{}" + durationText);
        String[] arr = durationText.split(":");
        //计算视频总时长(单位:秒),每20秒切一次
        int duration = 0;

        //计算小时单位的数量
        if (!"00".equals(arr[0])) {
            duration = (Integer.parseInt(arr[0]) * 3600);
        }
        if (!"00".equals(arr[1])) {
            duration += (Integer.parseInt(arr[1]) * 60);
        }
        if (!"00".equals(arr[2]) && !"00.00".equals(arr[2])) {
            String secondStr = arr[2];
            String[] split = secondStr.split("\\.");
            duration += Integer.parseInt(split[0]);
            if (!"00".equals(split[1])) {
                //带有毫秒的单位不为0,则默认将视频长度加1s
                duration++;
            }
        }
        log.info("视频时长为: 【{}】 秒", duration);
        return duration;
    }

exec方法

/**
     * 通过终端执行获取时长指令
     * 指令格式:ffprobe 视频路径
     *
     * @param dos 指令
     * @return 返回的文本
     */
    private static String execDos(String dos) {
        try {
            Runtime runtime = Runtime.getRuntime();
            Process process = runtime.exec(dos);
            process.waitFor();
            InputStream in = process.getErrorStream();
            //commons-io的v2.3依赖,org.apache.commons.io包
            return IOUtils.toString(in, StandardCharsets.UTF_8);
        } catch (InterruptedException | IOException e) {
            e.printStackTrace();
        }
        return null;
    }

waitVideoSegmentFinish方法

  • 等待视频切片完成的时间需要自己控制,主要是通过通过增加fragmentNum * 4中的4和Thread.sleep(3000)的3000来提高等待时间,lz这里测试的2核16G服务器可以使用代码中的参数,lz的生产环境2核16G内存占用85cpu占用20左右的状态下一个100多兆的视频需要将近十分钟,切分时间与视频的码率也有关系。视频切分好像还自带视频压缩功能,尽量保证需要处理的视频码率(视频大小除以视频时长,平均每秒的视频大小)在350kb/s码率之内,这个最好根据服务器配置自行配置。视频切分对服务器的CPU
  • 另外发现当视频长度小于20s时(切分视频的脚本中有一个参数是根据每次视频时长切分),即切片数量较小时,生成m3u8文件的时长特别长,lz测的切片数量为1时生成m3u8文件需要30s的时间,所以代码Thread.sleep(10000);最好判断一下切片数量,切片数量小的情况下程序等待时长需要延长到30S
/**
     * 等待视频切片完成
     *
     * @param storageFilePath 发生切片的路径
     * @param fragmentNum     计算出来的视频切片数量
     */
    public static void waitVideoSegmentFinish(String storageFilePath, int fragmentNum) {
        //为了防止死循环可以循环进入的次数,通过切片数量调整次数
        long start = System.currentTimeMillis();
        int entryTime = 0;
        do {
            if (entryTime > fragmentNum * 4) {
                //正常情况下每切一次耗时10秒左右,进入循环4次,总共数量为切片耗时:fragmentNum * 3 +10
                break;
            }
            try {
                Thread.sleep(3000);
                //增加进入的次数
                entryTime++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } while (getAllFileNameCountTsNumber(storageFilePath) < fragmentNum);

        //ts数量与计算的切片总数一样时再等10秒,等待切片数据写入完成
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        log.info("***本次切片耗时:【{}】秒***", (end - start) / 1000);
    }

getAllFileNameCountTsNumber方法

/**
     * 统计当前路径下的ts后缀文件数量,通过检测文件夹下总ts文件数量
     * 与计算的ts数量是否相同判断切割是否完成(切割后端ts总大小与原视频大小不一致)
     *
     * @param directoryPath 需要遍历的文件夹路径
     * @return 文件名称
     */
    private static int getAllFileNameCountTsNumber(String directoryPath) {
        //路径下的所有文件,不包括文件夹
        List<String> list = new ArrayList<>();
        File baseFile = new File(directoryPath);
        if (baseFile.isFile() || !baseFile.exists()) {
            return 0;
        }
        File[] files = baseFile.listFiles();
        for (File file : files) {
            if (!file.isDirectory()) {
                list.add(file.getName());
            }
        }

        //计算ts文件后缀数量
        int count = 0;
        for (String s : list) {
            int start = s.lastIndexOf(".") + 1;
            String suffix = s.substring(start);
            if ("ts".equals(suffix)) {
                count++;
            }
        }
        return count;
    }

getUploadPathList方法

/**
     * 获取指定目录下的ts、m3u8、enc.key文件的绝对路径列表
     *
     * @param basePath 基础路径
     * @return 路径集合
     */
    public static List<String> getUploadPathList(String basePath) {
        List<String> list = new ArrayList<>();
        File baseFile = new File(basePath);
        if (baseFile.isFile() || !baseFile.exists()) {
            return null;
        }
        File[] files = baseFile.listFiles();
        for (File file : files) {
            if (!file.isDirectory()) {
                String name = file.getName();
                int start = name.lastIndexOf(".") + 1;
                //文件后缀
                String suffix = name.substring(start);
                if ("ts".equals(suffix) || "m3u8".equals(suffix) || "key".equals(suffix)) {
                    //将需要上传的ts、m3u8、enc.key文件加入到list中
                    list.add(file.getAbsolutePath());
                }
            }
        }
        return list;
    }

uploadFileWithInputSteam方法

 /**
     * 上传文件流
     *
     * @param oranFileName 上传文件到OSS时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg
     * @param inputStream  来自本地的文件流
     */
    public static String uploadFileWithInputSteam(String bucketName, String oranFileName, InputStream inputStream) {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        // 上传文件流
        try {
            //上传到OSS
            ossClient.putObject(bucketName, objectName, inputStream);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        // 关闭OSSClient。
        ossClient.shutdown();
        //返回文件在服务器上的全路径+名称
        return getRealName(bucketName, objectName);
    }

用来处理中文转汉语拼音的工具类

package com.luntek.commons.util;

/**
 * 汉语拼音工具类
 *
 * @Date 2019/10/21 0021 下午 7:10
 * @Created by Czw
 */

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;

public class HanYuPinyinHelper {

    /**
     * MaNongXF
     * 将文字转为汉语拼音
     *
     * @param ChineseLanguage 要转成拼音的中文
     */
    public String toHanyuPinyin(String ChineseLanguage) {
        char[] cl_chars = ChineseLanguage.trim().toCharArray();
        String hanyupinyin = "";
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
        defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);// 输出拼音全部小写
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);// 不带声调
        defaultFormat.setVCharType(HanyuPinyinVCharType.WITH_V);
        try {
            for (int i = 0; i < cl_chars.length; i++) {
                if (String.valueOf(cl_chars[i]).matches("[\u4e00-\u9fa5]+")) {// 如果字符是中文,则将中文转为汉语拼音
                    hanyupinyin += PinyinHelper.toHanyuPinyinStringArray(cl_chars[i], defaultFormat)[0];
                } else {// 如果字符不是中文,则不转换
                    hanyupinyin += cl_chars[i];
                }
            }
        } catch (BadHanyuPinyinOutputFormatCombination e) {
            System.out.println("字符不能转成汉语拼音");
        }
        return hanyupinyin;
    }

    /**
     * -
     * 将转换的拼音变成大写
     *
     * @auther Czw
     * @date 2019/10/21 0021 下午 7:17
     */
    public static String getFirstLettersUp(String ChineseLanguage) {
        return getFirstLetters(ChineseLanguage, HanyuPinyinCaseType.UPPERCASE);
    }

    /**
     * -
     * 将转换的拼音变成小写
     *
     * @auther Czw
     * @date 2019/10/21 0021 下午 7:17
     */
    public static String getFirstLettersLo(String ChineseLanguage) {
        return getFirstLetters(ChineseLanguage, HanyuPinyinCaseType.LOWERCASE);
    }

    /**
     * 将转换的拼音变成大写或者小写
     *
     * @auther Czw
     * @date 2019/10/21 0021 下午 7:17
     */
    public static String getFirstLetters(String ChineseLanguage, HanyuPinyinCaseType caseType) {
        char[] cl_chars = ChineseLanguage.trim().toCharArray();
        String hanyupinyin = "";
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
        defaultFormat.setCaseType(caseType);// 输出拼音全部大写
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);// 不带声调
        try {
            for (int i = 0; i < cl_chars.length; i++) {
                String str = String.valueOf(cl_chars[i]);
                if (str.matches("[\u4e00-\u9fa5]+")) {// 如果字符是中文,则将中文转为汉语拼音,并取第一个字母
                    hanyupinyin += PinyinHelper.toHanyuPinyinStringArray(cl_chars[i], defaultFormat)[0].substring(0, 1);
                } else if (str.matches("[0-9]+")) {// 如果字符是数字,取数字
                    hanyupinyin += cl_chars[i];
                } else if (str.matches("[a-zA-Z]+")) {// 如果字符是字母,取字母
                    hanyupinyin += cl_chars[i];
                } else {// 否则不转换
                    hanyupinyin += cl_chars[i];//如果是标点符号的话,带着
                }
            }
        } catch (BadHanyuPinyinOutputFormatCombination e) {
            System.out.println("字符不能转成汉语拼音");
        }
        return hanyupinyin;
    }

    public static String getPinyinString(String ChineseLanguage) {
        char[] cl_chars = ChineseLanguage.trim().toCharArray();
        String hanyupinyin = "";
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
        defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);// 输出拼音全部大写
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);// 不带声调
        try {
            for (int i = 0; i < cl_chars.length; i++) {
                String str = String.valueOf(cl_chars[i]);
                if (str.matches("[\u4e00-\u9fa5]+")) {// 如果字符是中文,则将中文转为汉语拼音,并取第一个字母
                    hanyupinyin += PinyinHelper.toHanyuPinyinStringArray(
                            cl_chars[i], defaultFormat)[0];
                } else if (str.matches("[0-9]+")) {// 如果字符是数字,取数字
                    hanyupinyin += cl_chars[i];
                } else if (str.matches("[a-zA-Z]+")) {// 如果字符是字母,取字母

                    hanyupinyin += cl_chars[i];
                } else {// 否则不转换
                }
            }
        } catch (BadHanyuPinyinOutputFormatCombination e) {
            System.out.println("字符不能转成汉语拼音");
        }
        return hanyupinyin;
    }

    /**
     * 取第一个汉字的第一个字符
     *
     * @return String
     * @throws
     * @Title: getFirstLetter
     */
    public static String getFirstLetter(String ChineseLanguage) {
        char[] cl_chars = ChineseLanguage.trim().toCharArray();
        String hanyupinyin = "";
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
        defaultFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);// 输出拼音全部大写
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);// 不带声调
        try {
            String str = String.valueOf(cl_chars[0]);
            if (str.matches("[\u4e00-\u9fa5]+")) {// 如果字符是中文,则将中文转为汉语拼音,并取第一个字母
                hanyupinyin = PinyinHelper.toHanyuPinyinStringArray(
                        cl_chars[0], defaultFormat)[0].substring(0, 1);
                ;
            } else if (str.matches("[0-9]+")) {// 如果字符是数字,取数字
                hanyupinyin += cl_chars[0];
            } else if (str.matches("[a-zA-Z]+")) {// 如果字符是字母,取字母

                hanyupinyin += cl_chars[0];
            } else {// 否则不转换

            }
        } catch (BadHanyuPinyinOutputFormatCombination e) {
            System.out.println("字符不能转成汉语拼音");
        }
        return hanyupinyin;
    }

    public static void main(String[] args) {
        HanYuPinyinHelper hanyuPinyinHelper = new HanYuPinyinHelper();
        System.out.println(hanyuPinyinHelper.toHanyuPinyin("浙江机电职业技术学院aaa"));
        System.out.println(hanyuPinyinHelper.getFirstLettersLo("浙江机电职业技术学院"));
    }
    //打印结果:ceshiceshi
}


代码编写与整理都花了很长时间、代码中有很多注释,如果漏有什么地方没有分享到可以给我留言我一般工作日一天之内就会回复,如果对你有帮助希望点个赞哦

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一米阳光zw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值