vue+java实现语音转文字思路

news2024/11/23 19:50:28

思路:

前端录音生成wav文件后端去解析

技术:

后端:
Vosk是一个离线开源语音识别工具。它可以识别16种语言,包括中文。
API接口,让您可以只用几行代码,即可迅速免费调用、体验功能。
目前支持 WAV声音文件格式,支持中英文等18种语言。
前端:
js-audio-recorder 录音组件

资料:

下载vosk语言模型:
在这里插入图片描述

springboot整合vosk实现简单的语音识别功能
javaswing窗体

问题:

就是录音组件会要求后端使用https协议,生产环境必须将后端http转https,测试环境中有以下两种方法第一种录音只能在(http://localhost:项目端口)中使用,第二种使用谷歌配置网站具有使用录音权限

后端:

在这里插入图片描述

依赖:

  <!-- 语音识别 -->
        <!-- 获取音频信息 -->
        <dependency>
            <groupId>org</groupId>
            <artifactId>jaudiotagger</artifactId>
            <version>2.0.3</version>
        </dependency>
        
        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna</artifactId>
            <version>5.13.0</version>
        </dependency>
        <dependency>
            <groupId>com.alphacephei</groupId>
            <artifactId>vosk</artifactId>
            <version>0.3.45</version>
        </dependency>

        <!-- JAVE2(Java音频视频编码器)库是ffmpeg项目上的Java包装器。 -->
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-core</artifactId>
            <version>3.1.1</version>
        </dependency>

        <!-- 在windows上开发 开发机可实现压缩效果 window64位 -->
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-nativebin-win32</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-nativebin-win64</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

代码(我会按代码的调用顺序展示)

package com.rouyi.yuyin.model;

public class VoskResult {
    private String text;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

}

vosk模型加载(将你从官网下的语言模型放到你的项目中并解压,修改下面的modelStr的值)
在这里插入图片描述

package com.rouyi.yuyin.model;

import org.vosk.LibVosk;
import org.vosk.LogLevel;
import org.vosk.Model;

import java.io.IOException;

/**
 * vosk模型加载
 * @author zhou
 */
public class VoskModel {

    /**
     * 3. 使用 volatile 保证线程安全
     * 禁止指令重排
     * 保证可见性
     * 不保证原子性
     */
    private static volatile VoskModel instance;

    private Model voskModel;

    public Model getVoskModel() {
        return voskModel;
    }

    /**
     * 1.私有构造函数
     */
    private VoskModel() {
        System.out.println("SingleLazyPattern实例化了");
        //String modelStr = "D:\\work\\project\\fjdci-vosk\\src\\main\\resources\\vosk-model-small-cn-0.22";
        String modelStr = "H:\\afterProject\\qiyedianzixuke\\RuoYi-Cloud\\ruoyi-modules\\yuyinshibie\\src\\main\\resources\\lib\\vosk-model-cn-0.22";
        try {
            voskModel = new Model(modelStr);
            LibVosk.setLogLevel(LogLevel.INFO);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 2.通过静态方法获取一个唯一实例
     * DCL 双重检查锁定 (Double-CheckedLocking)
     * 在多线程情况下保持⾼性能
     */
    public static VoskModel getInstance() {
        if (instance == null) {
            synchronized (VoskModel.class) {
                if (instance == null) {
                    // 1. 分配内存空间 2、执行构造方法,初始化对象 3、把这个对象指向这个空间
                    instance = new VoskModel();
                }
            }
        }
        return instance;
    }

    /**
     * 多线程测试加载
     * @param args
     */
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                VoskModel.getInstance();
            }).start();
        }
    }


}


package com.rouyi.yuyin.model;



import ws.schild.jave.Encoder;
import ws.schild.jave.EncoderException;
import ws.schild.jave.InputFormatException;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.encode.AudioAttributes;
import ws.schild.jave.encode.EncodingAttributes;
import ws.schild.jave.info.AudioInfo;
import ws.schild.jave.info.MultimediaInfo;

import java.io.File;

public class Jave2Util {

    /**
     * @param src      来源文件路径
     * @param target   目标文件路径
     * @param offset   设置起始偏移量(秒)
     * @param duration 设置切片的音频长度(秒)
     * @throws EncoderException
     */
    public static void cut(String src, String target, Float offset, Float duration) throws EncoderException {

        File targetFile = new File(target);
        if (targetFile.exists()) {
            targetFile.delete();
        }

        File srcFile = new File(src);
        MultimediaObject srcMultiObj = new MultimediaObject(srcFile);
        MultimediaInfo srcMediaInfo = srcMultiObj.getInfo();

        Encoder encoder = new Encoder();

        EncodingAttributes encodingAttributes = new EncodingAttributes();
        //设置起始偏移量(秒)
        encodingAttributes.setOffset(offset);
        //设置切片的音频长度(秒)
        encodingAttributes.setDuration(duration);
        // 输入格式
        encodingAttributes.setInputFormat("wav");

        //设置音频属性
        AudioAttributes audio = new AudioAttributes();
        audio.setBitRate(srcMediaInfo.getAudio().getBitRate());
        //audio.setSamplingRate(srcMediaInfo.getAudio().getSamplingRate());
        // 转换为16KHZ 满足vosk识别的标准
        audio.setSamplingRate(16000);
        audio.setChannels(srcMediaInfo.getAudio().getChannels());
        //如果截取的时候,希望同步调整编码,可以设置不同的编码
//        audio.setCodec("pcm_u8");
        //audio.setCodec(srcMediaInfo.getAudio().getDecoder().split(" ")[0]);
        encodingAttributes.setAudioAttributes(audio);
        //写文件
        encoder.encode(srcMultiObj, new File(target), encodingAttributes);
    }

    /**
     * 转化音频格式
     *
     * @param oldFormatPath : 原音乐路径
     * @param newFormatPath : 目标音乐路径
     * @return
     */
    public static boolean transforMusicFormat(String oldFormatPath, String newFormatPath) {
        File source = new File(oldFormatPath);
        File target = new File(newFormatPath);
        // 音频转换格式类
        Encoder encoder = new Encoder();
        // 设置音频属性
        AudioAttributes audio = new AudioAttributes();
        audio.setCodec(null);
        // 设置转码属性
        EncodingAttributes attrs = new EncodingAttributes();
        attrs.setInputFormat("wav");
        attrs.setAudioAttributes(audio);
        try {
            encoder.encode(new MultimediaObject(source), target, attrs);
            System.out.println("传唤已完成...");
            return true;
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InputFormatException e) {
            e.printStackTrace();
        } catch (EncoderException e) {
            e.printStackTrace();
        }
        return false;
    }



    public static void main(String[] args) throws EncoderException {

        String src = "D:\\fjFile\\annex\\xwbl\\ly8603f22f24e0409fa9747d50a78ff7e5.wav";
        String target = "D:\\fjFile\\annex\\xwbl\\tem_2.wav";

        Jave2Util.cut(src, target, 0.0F, 60.0F);

        String inputFormatPath = "D:\\fjFile\\annex\\xwbl\\ly8603f22f24e0409fa9747d50a78ff7e5.m4a";
        String outputFormatPath = "D:\\fjFile\\annex\\xwbl\\ly8603f22f24e0409fa9747d50a78ff7e5.wav";

        info(inputFormatPath);

        // audioEncode(inputFormatPath, outputFormatPath);


    }

    /**
     * 获取音频文件的编码信息
     *
     * @param filePath
     * @throws EncoderException
     */
    private static void info(String filePath) throws EncoderException {
        File file = new File(filePath);
        MultimediaObject multimediaObject = new MultimediaObject(file);
        MultimediaInfo info = multimediaObject.getInfo();
        // 时长
        long duration = info.getDuration();
        String format = info.getFormat();
        // format:mov
        System.out.println("format:" + format);
        AudioInfo audio = info.getAudio();
        // 它设置将在重新编码的音频流中使用的音频通道数(1 =单声道,2 =立体声)。如果未设置任何通道值,则编码器将选择默认值。
        int channels = audio.getChannels();
        // 它为新的重新编码的音频流设置比特率值。如果未设置比特率值,则编码器将选择默认值。
        // 该值应以每秒位数表示。例如,如果您想要128 kb / s的比特率,则应调用setBitRate(new Integer(128000))。
        int bitRate = audio.getBitRate();
        // 它为新的重新编码的音频流设置采样率。如果未设置采样率值,则编码器将选择默认值。该值应以赫兹表示。例如,如果您想要类似CD
        // 采样率、音频采样级别 16000 = 16KHz
        int samplingRate = audio.getSamplingRate();

        // 设置音频音量
        // 可以调用此方法来更改音频流的音量。值为256表示音量不变。因此,小于256的值表示音量减小,而大于256的值将增大音频流的音量。
        // setVolume(Integer volume)

        String decoder = audio.getDecoder();

        System.out.println("声音时长:毫秒" + duration);
        System.out.println("声道:" + channels);
        System.out.println("bitRate:" + bitRate);
        System.out.println("samplingRate 采样率、音频采样级别 16000 = 16KHz:" + samplingRate);
        // aac (LC) (mp4a / 0x6134706D)
        System.out.println("decoder:" + decoder);
    }

    /**
     * 音频格式转换
     * @param inputFormatPath
     * @param outputFormatPath
     * @return
     */
    public static boolean audioEncode(String inputFormatPath, String outputFormatPath) {
        String outputFormat = getSuffix(outputFormatPath);
        String inputFormat = getSuffix(inputFormatPath);
        File source = new File(inputFormatPath);
        File target = new File(outputFormatPath);
        try {
            MultimediaObject multimediaObject = new MultimediaObject(source);
            // 获取音频文件的编码信息
            MultimediaInfo info = multimediaObject.getInfo();
            AudioInfo audioInfo = info.getAudio();
            //设置音频属性
            AudioAttributes audio = new AudioAttributes();
            audio.setBitRate(audioInfo.getBitRate());
            audio.setSamplingRate(audioInfo.getSamplingRate());
            audio.setChannels(audioInfo.getChannels());
            // 设置转码属性
            EncodingAttributes attrs = new EncodingAttributes();
            attrs.setInputFormat(inputFormat);
            attrs.setOutputFormat(outputFormat);
            attrs.setAudioAttributes(audio);
            // 音频转换格式类
            Encoder encoder = new Encoder();
            // 进行转换
            encoder.encode(new MultimediaObject(source), target, attrs);
            return true;
        } catch (IllegalArgumentException | EncoderException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 获取文件路径的.后缀
     * @param outputFormatPath
     * @return
     */
    private static String getSuffix(String outputFormatPath) {
        return outputFormatPath.substring(outputFormatPath.lastIndexOf(".") + 1);
    }


}


修改wavFilePath的值为你的wav格式的文件所在路径,wav文件可以自行使用手机自带的录音功能去生成最后点击main方法就可以测试了,如果需要与前台对接请自行修改为接口

package com.rouyi.yuyin.model;


import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.vosk.Model;
import org.vosk.Recognizer;
import ws.schild.jave.EncoderException;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.info.AudioInfo;
import ws.schild.jave.info.MultimediaInfo;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Slf4j
@Component
public class VoiceUtil {

    public static void main(String[] args) throws EncoderException {
        String wavFilePath = "H:\\afterProject\\qiyedianzixuke\\RuoYi-Cloud\\ruoyi-modules\\yuyinshibie\\src\\main\\resources\\audio\\11月7日 下午4点10分.wav";
        // 秒
        long cutDuration = 20;
        String waveForm = acceptWaveForm( wavFilePath, cutDuration);
        System.out.println( waveForm );
    }

    /**
     * 对Wav格式音频文件进行语音识别翻译
     *
     * @param wavFilePath
     * @param cutDuration
     * @return
     * @throws EncoderException
     */
    private static String acceptWaveForm(String wavFilePath, long cutDuration) throws EncoderException {
        // 判断视频的长度
        long startTime = System.currentTimeMillis();
        MultimediaObject multimediaObject = new MultimediaObject(new File(wavFilePath));
        MultimediaInfo info = multimediaObject.getInfo();
        // 时长/毫秒
        long duration = info.getDuration();
        AudioInfo audio = info.getAudio();
        // 通道数
        int channels = audio.getChannels();
        // 秒
        long offset = 0;
        long forNum = (duration / 1000) / cutDuration;
        if (duration % (cutDuration * 1000) > 0) {
            forNum = forNum + 1;
        }
        // 进行切块处理
        List<String> strings = cutWavFile(wavFilePath, cutDuration, offset, forNum);
        // 循环进行翻译
        StringBuilder result = new StringBuilder();
        for (String string : strings) {
            File f = new File(string);
            result.append(VoiceUtil.getRecognizerResult(f, channels));
        }
        long endTime = System.currentTimeMillis();
        String msg = "耗时:" + (endTime - startTime) + "ms";
        System.out.println(msg);
        return result.toString();
    }

    /**
     * 对wav进行切块处理
     *
     * @param wavFilePath 处理的wav文件路径
     * @param cutDuration 切割的固定长度/秒
     * @param offset      设置起始偏移量(秒)
     * @param forNum      切块的次数
     * @return
     * @throws EncoderException
     */
    private static List<String> cutWavFile(String wavFilePath, long cutDuration, long offset, long forNum) throws EncoderException {
        UUID uuid = UUID.randomUUID();
        // 大文件切割为固定时长的小文件
        List<String> strings = new ArrayList<>();
        for (int i = 0; i < forNum; i++) {
            String target = "D:\\fjFile\\annex\\xwbl\\" + uuid + "\\" + i + ".wav";
            Float offsetF = Float.valueOf(String.valueOf(offset));
            Float cutDurationF = Float.valueOf(String.valueOf(cutDuration));
            Jave2Util.cut(wavFilePath, target, offsetF, cutDurationF);
            offset = offset + cutDuration;
            strings.add(target);
        }
        return strings;
    }

    /**
     * 进行翻译
     *
     * @param f
     * @param channels
     */
    public static String getRecognizerResult(File f, int channels) {
        StringBuilder result = new StringBuilder();
        Model voskModel = VoskModel.getInstance().getVoskModel();
        // 采样率为音频采样率的声道倍数
        log.info("====加载完成,开始分析====");
        try (
                Recognizer recognizer = new Recognizer(voskModel, 16000 * channels);
                InputStream ais = new FileInputStream(f)
        ) {
            int nbytes;
            byte[] b = new byte[4096];
            while ((nbytes = ais.read(b)) >= 0) {
                if (recognizer.acceptWaveForm(b, nbytes)) {
                    // 返回语音识别结果
                    result.append(getResult(recognizer.getResult()));
                }
            }
            // 返回语音识别结果。和结果一样,但不要等待沉默。你通常在流的最后调用它来获得音频的最后部分。它刷新功能管道,以便处理所有剩余的音频块。
            result.append(getResult(recognizer.getFinalResult()));
            log.info("识别结果:{}", result.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result.toString();
    }

    /**
     * 获取返回结果
     *
     * @param result
     * @return
     */
    private static String getResult(String result) {
        VoskResult vr=JSON.parseObject(result,VoskResult.class);
        return  Optional.ofNullable(vr).map(VoskResult::getText).orElse("");

    }

}

在这里插入图片描述

vue:

这里呢我前端也没整完前端这里生成录音后传给后台就可以了,后台用上面的Java代码一解析,别说我懒,做东西还不做完美,想啥呢搬砖很累的哈哈!!!

<template>
    <div style="padding: 20px;">

          <h3>录音上传</h3>

     <div style="font-size:14px">

           <h3>录音时长:{{ recorder && recorder.duration.toFixed(4) }}</h3>

            <br />

            <el-button type="primary" @click="handleStart">开始录音</el-button>

            <el-button type="info" @click="handlePause">暂停录音</el-button>

            <el-button type="success" @click="handleResume">继续录音</el-button>

            <el-button type="warning" @click="handleStop">停止录音</el-button>
            <el-button type="error" @click="handleDestroy">销毁录音</el-button>
            <el-button type="primary" @click="uploadRecord">上传</el-button>

            <!-- <br />

            <br />

            <h3>

                播放时长:{{

                    recorder &&

                    (playTime > recorder.duration

                        ? recorder.duration.toFixed(4)

                        : playTime.toFixed(4))

                }}

                </h3>

            <br />

            <el-button type="primary" @click="handlePlay">播放录音</el-button>

            <el-button type="info" @click="handlePausePlay">暂停播放</el-button>

            <el-button type="success" @click="handleResumePlay">继续播放</el-button>

            <el-button type="warning" @click="handleStopPlay">停止播放</el-button>

            <el-button type="error" @click="handleDestroy">销毁录音</el-button>

            <el-button type="primary" @click="uploadRecord">上传</el-button> -->

            </div>

        </div>
</template>
    
<script>

import Recorder from 'js-audio-recorder'

export default {

    data() {

        return {

            recorder: null,

            playTime: 0,

            timer: null,

            src: null

        }

    },

    created() {

        this.recorder = new Recorder()

    },

    methods: {

        // 开始录音

        handleStart() {

            this.recorder = new Recorder()

            Recorder.getPermission().then(() => {

                console.log('开始录音')

                this.recorder.start() // 开始录音

            }, (error) => {

                this.$message({

                    message: '请先允许该网页使用麦克风',

                    type: 'info'

                })

                console.log(`${error.name} : ${error.message}`)

            })

        },

        handlePause() {

            console.log('暂停录音')

            this.recorder.pause() // 暂停录音

        },

        handleResume() {

            console.log('恢复录音')

            this.recorder.resume() // 恢复录音

        },

        handleStop() {

            console.log('停止录音')

            this.recorder.stop() // 停止录音

        },

        handlePlay() {

            console.log('播放录音')

            console.log(this.recorder)

            this.recorder.play() // 播放录音

            // 播放时长

            this.timer = setInterval(() => {

                try {

                    this.playTime = this.recorder.getPlayTime()

                } catch (error) {

                    this.timer = null

                }

            }, 100)

        },

        handlePausePlay() {

            console.log('暂停播放')

            this.recorder.pausePlay() // 暂停播放

            // 播放时长

            this.playTime = this.recorder.getPlayTime()

            this.time = null

        },

        handleResumePlay() {

            console.log('恢复播放')

            this.recorder.resumePlay() // 恢复播放

            // 播放时长

            this.timer = setInterval(() => {

                try {

                    this.playTime = this.recorder.getPlayTime()

                } catch (error) {

                    this.timer = null

                }

            }, 100)

        },

        handleStopPlay() {

            console.log('停止播放')

            this.recorder.stopPlay() // 停止播放

            // 播放时长

            this.playTime = this.recorder.getPlayTime()

            this.timer = null

        },

        handleDestroy() {

            console.log('销毁实例')

            this.recorder.destroy() // 毁实例

            this.timer = null

        },

        uploadRecord() {

            if (this.recorder == null || this.recorder.duration === 0) {

                this.$message({

                    message: '请先录音',

                    type: 'error'

                })

                return false

            }

            this.recorder.pause() // 暂停录音

            this.timer = null

            console.log('上传录音')// 上传录音

            const formData = new FormData()

            const blob = this.recorder.getWAVBlob()// 获取wav格式音频数据

            // 此处获取到blob对象后需要设置fileName满足当前项目上传需求,其它项目可直接传把blob作为file塞入formData

            const newbolb = new Blob([blob], { type: 'audio/wav' })

            const fileOfBlob = new File([newbolb], new Date().getTime() + '.wav')

            formData.append('file', fileOfBlob)

            const url = window.URL.createObjectURL(fileOfBlob)

            this.src = url

            // const axios = require('axios')

            // axios.post(url, formData).then(res => {

            //console.log(res.data.data[0].url)

            // })

        }

    }

}

</script>
    
     
    
            
    
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1184646.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MobaXterm配置SSHTunnel

本地与远程服务器之间存在防火墙&#xff0c;防火墙只允许SSH端口通过&#xff0c;为访问远程服务器&#xff0c;我们可以借助MobaXterm来与SSH服务器建立隧道&#xff0c;使得防火墙外的用户能够访问远程服务器 配置 打开SSHTunnel 新建SSH tunnel 点击开启就生效了&…

数据链路相关技术

文章目录 数据链路相关技术MAC地址半双工通信与全双工通信共享介质型网络非共享介质网络根据MAC地址转发以太网无线通信 数据链路相关技术 MAC地址 MAC地址用于识别数据链路中互连的节点&#xff0c;以太网或FDDI中&#xff0c;根据IEEE802.3的规范使用MAC地址。其中IEEE指的…

详解交叉验证中【KFold】【Stratified-KFold】【StratifiedShuffleSplit】的区别

交叉验证是一种统计分析方法&#xff0c;它的目的是通过在同一数据集上重复并分割训练和测试数据&#xff0c;来评估机器学习模型的性能。以下是​这三种交叉验证方法的区别&#xff1a; KFold&#xff08;K-折叠&#xff09; 在KFold交叉验证中&#xff0c;原始数据集被分为K个…

思杰Citrix将全面退出中国市场,是真的吗?

引言&#xff1a;国内虚拟化市场依然有潜力&#xff0c;转换思路继续开发&#xff0c;这个可能性最大。 【科技明说 &#xff5c; 热点关注】 业内讨论说&#xff0c;虚拟化大佬思杰Citrix将全面退出国内市场&#xff0c;不知道消息是否属实&#xff1f; 另外假如消息属实的话…

串口调试助手和网络调试助手使用总结

串口调试助手和网络调试助手是用的比较多的两款工具。 先来看看串口调试助手。 本人用的比较多的串口助手是正点原子的XCOM以及大虾丁丁的SSCOM 首先&#xff0c;解决下串口收发时的统计问题。 注意&#xff1a;这里统计的单位是字节。 串口工具发送时&#xff0c;就只统计你…

05-MySQL-进阶-存储引擎索引SQL优化

一、存储引擎 涉及资料 链接&#xff1a;https://pan.baidu.com/s/1M1oXN_pH3RGADx90ZFbfLQ?pwdCoke 提取码&#xff1a;Coke ①&#xff1a;MySQL体系结构 1.连接层 最上层是一些客户端和链接服务&#xff0c;包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于 T…

掌握未来:PureBasic for Mac引领BASIC语言编辑器的新潮流

PureBasic for Mac是一种创新的BASIC语言编辑器&#xff0c;它赋予了编程更多的可能性。在这个充满机遇的时代&#xff0c;掌握编程就等于掌握了一种强大的工具&#xff0c;能够更好地理解和塑造世界。而PureBasic for Mac&#xff0c;正是这样一个让你轻松上手&#xff0c;高效…

超图Web许可无法访问

1. 报错 docker 容器(7f6f88XXXXX)找不到许可&#xff0c;查看日志&#xff0c;发现报错日志 2. 原因&#xff1a; 查看管理页面&#xff0c;发现许可被172.17.0.8占用 根据容器id寻找容器&#xff0c;找不到&#xff0c;猜测可能是以前删除过的容器&#xff0c;占用了名额 解决…

Flink -- 并行度

1、并行度&#xff1a; 对于一个Flink任务是有Source、Transformation和Sink等任务组成&#xff0c;一个任务由多个并行实例来执行&#xff0c;一个任务的并行实例数目被称为该任务的并行度。 2、TaskManager和Solt Flink是一个分布式流处理框架&#xff0c;它基于TaskManager…

高效自学-网络安全(黑客技术)

一、网络安全应该怎么学&#xff1f; 1.计算机基础需要过关 这一步跟网安关系暂时不大&#xff0c;是进入it行业每个人都必须掌握的基础能力。 计算机网络计算机操作系统算法与数据架构数据库 Tips:不用非要钻研至非常精通&#xff0c;可以与学习其他课程同步进行。 2.渗透技…

定时删除指定目录下的文件,文件名按时间有规律,定时删除过期文件

#!/bin/bash# 指定目录 directory"path/to/directory"# 当前日期 current_date$(date %Y-%m-%d)# 计算7天前的日期 seven_days_ago$(date -d "$current_date -7 days" %Y-%m-%d)# 遍历目录中的文件&#xff0c;按文件名过滤并删除7天前的文件 for file in …

双11“万亿交易额”背后,浪潮信息助力银行扛住交易洪流

双十一&#xff0c;不仅是网络购物的狂欢&#xff0c;更是中国支付清算业务的大考。 举目望去&#xff0c;双十一的台前幕后可谓是“不一样的精彩”。一方面台前的主角是消费者&#xff0c;全球超200个国家和地区的人们捧着手机、电脑&#xff0c;在阿里、京东、抖音、拼多多等…

第七章 块为结构建模 P2|系统建模语言SysML实用指南学习

仅供个人学习记录 流建模 对系统不同组成之间的流做出定义可提供它们之间交互作用的抽象视图 项是定义为流动事物的通用术语。流属性定义了该块可以流入或流出的项 为流动的项建模 项item用于描述一类流动的实体&#xff0c;可以是物质流&#xff08;如物质和能量&#xff…

SpectralDiff论文阅读笔记

高光谱图像分类是遥感领域的一个重要问题,在地球科学中有着广泛的应用。近年来,人们提出了大量基于深度学习的HSI分类方法。然而,现有方法处理高维、高冗余和复杂数据的能力有限,这使得捕获数据的光谱空间分布和样本之间的关系具有挑战性。 为了解决这一问题,我们提出了一…

【Unity之UI编程】在Unity中如何打图集,来降低DrowCall

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;UI_…

win11右键菜单切换为win10样式

最近更新了win11&#xff0c;右键菜单和我的使用习惯很不一致&#xff0c;于是打算切换回去&#xff0c;看了网上好多教程&#xff0c;决定使用以下方法&#xff0c;记录以下&#xff0c;防止忘记。 一、切换win10 1、管理员运行命令提示符 2、输入 reg add "HKCU\Soft…

P1506 拯救oibh总部(BFS洪水灌溉)

题目&#xff1a; 样例1&#xff1a; 输入 4 5 00000 00*00 0*0*0 00*00 输出 1 样例2&#xff1a; 输入 5 5 ***** *0*0* **0** *0*0* ***** 输出 5 思路&#xff1a; 洪水灌溉&#xff0c;思路&#xff1a;给该图外面包围一圈可遍历的的点&#xff0c;作为引流灌溉。 BFS…

python自动化测试(3)- 自动化框架及工具

1、概述 手续的关于测试的方法论&#xff0c;都是建立在之前的文章里面提到的观点&#xff1a; 功能测试不建议做自动化接口测试性价比最高接口测试可以做自动化 后面所谈到的 测试自动化 也将围绕着 接口自动化 来介绍。 本系列选择的测试语言是 python 脚本语言。由于其官…

node项目调试

node 版本要在16.8版本或以上&#xff0c;12点几没有这个调试功能 ctrlshiftp debug: toggle auto attach 将自动附加打开&#xff0c;打断点&#xff0c;然后执行命令 node --inspect ./bin/mvc.js,然后就进入调试