FFmpeg获取视频详情

news2024/12/23 18:26:17

话不多说,直接上代码:

pom依赖:

        <!--视频多媒体工具包 包含 FFmpeg、OpenCV-->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv-platform</artifactId>
            <version>1.5.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>16</version>
        </dependency>

工具类:

package com.example.mybatisdemo02.util;

import com.example.mybatisdemo02.model.VideoInfoVO;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.Stage;
import org.apache.commons.lang.StringUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;
import javafx.scene.control.Button;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.util.UUID;

public class FFmpegUtil {

    private static final String IMAGE_SUFFIX = "png";

    /**
     * 获取视频时长,单位为秒S
     * @return
     */
    public static long getVideoTime( String localPath) throws FrameGrabber.Exception {
//        String localPath = "C:/Users/Administrator/Desktop/dab2d14cad0244229e228e7bf297dd9a.flv";
        FFmpegFrameGrabber grabber = FFmpegFrameGrabber.createDefault(localPath);
        grabber.start();
        long duration = grabber.getLengthInTime() / (1000 * 1000);
        grabber.stop();
        System.out.println("此视频时长(s/秒):"+duration);
        return duration;
    }
    /**
     * 获取视频帧图片
     * @param file 视频源
     * @param number 第几帧
     * @param dir 文件存放根目录
     * @param args 文件存放根目录
     * @return
     */
    @SuppressWarnings("resource")
    public static String videoImage(File file, Integer number, String dir, String args) {
        String picPath = StringUtils.EMPTY;
        FFmpegFrameGrabber ff = new FFmpegFrameGrabber(file);
        try {
            ff.start();
            int i = 0;
            int length = ff.getLengthInFrames();
            Frame frame = null;
            while (i < length) {
                frame = ff.grabFrame();
                //截取第几帧图片
                if ((i > number) && (frame.image != null)) {
                    //获取生成图片的路径
                    picPath = getImagePath(args);
                    //执行截图并放入指定位置
                    doExecuteFrame(frame, dir + File.separator + picPath);
                    break;
                }
                i++;
            }
            ff.stop();
        } catch (FrameGrabber.Exception e) {
            e.printStackTrace();
        }
        return picPath;
    }

    /**
     * 截取缩略图
     * @param frame
     * @param targerFilePath 图片存放路径
     */
    public static void doExecuteFrame(Frame frame, String targerFilePath) {
        //截取的图片
        if (null == frame || null == frame.image) {
            return;
        }
        Java2DFrameConverter converter = new Java2DFrameConverter();
        BufferedImage srcImage = converter.getBufferedImage(frame);
        int srcImageWidth = srcImage.getWidth();
        int srcImageHeight = srcImage.getHeight();
        //对帧图片进行等比例缩放(缩略图)
        int width = 480;
        int height = (int) (((double) width / srcImageWidth) * srcImageHeight);
        BufferedImage thumbnailImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
        thumbnailImage.getGraphics().drawImage(srcImage.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);

        File output = new File(targerFilePath);
        try {
            ImageIO.write(thumbnailImage, IMAGE_SUFFIX, output);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成图片的相对路径
     * @param args 传入生成图片的名称、为空则用UUID命名
     * @return 例如 upload/images.png
     */
    public static String getImagePath(String args) {
        if (StringUtils.isNotEmpty(args)) {
            return args + "." + IMAGE_SUFFIX;
        }
        return getUUID() + "." + IMAGE_SUFFIX;
    }

    /**
     * 生成唯一的uuid
     * @return uuid
     */
    public static String getUUID(){
        return UUID.randomUUID().toString().replace("-","");
    }

    /**
     * 时长格式换算
     * @param duration 时长
     * @return HH:mm:ss
     */
    public static String formatDuration(Long duration) {
        String formatTime = StringUtils.EMPTY;
        double time = Double.valueOf(duration);
        if (time > -1) {
            int hour = (int) Math.floor(time / 3600);
            int minute = (int) (Math.floor(time / 60) % 60);
            int second = (int) (time % 60);

            if (hour < 10) {
                formatTime = "0";
            }
            formatTime += hour + ":";

            if (minute < 10) {
                formatTime += "0";
            }
            formatTime += minute + ":";

            if (second < 10) {
                formatTime += "0";
            }
            formatTime += second;
        }
        return formatTime;
    }

    /**
     * 获取图片大小Kb
     * @param urlPath
     * @return
     */
    public static String getImageSize(String urlPath){
        // 得到数据
        byte[] imageFromURL = getImageFromURL(urlPath);
        // 转换
        String byte2kb = bytes2kb(imageFromURL.length);
        return byte2kb;
    }

    /**
     * 根据图片地址获取图片信息
     *
     * @param urlPath 网络图片地址
     * @return
     */
    public static byte[] getImageFromURL(String urlPath) {
        // 字节数组
        byte[] data = null;
        // 输入流
        InputStream is = null;
        // Http连接对象
        HttpURLConnection conn = null;
        try {
            // Url对象
            URL url = new URL(urlPath);
            // 打开连接
            conn = (HttpURLConnection) url.openConnection();
            // 打开读取 写入是setDoOutput
//	        conn.setDoOutput(true);
            conn.setDoInput(true);
            // 设置请求方式
            conn.setRequestMethod("GET");
            // 设置超时时间
            conn.setConnectTimeout(6000);
            // 得到访问的数据流
            is = conn.getInputStream();
            // 验证访问状态是否是200 正常
            if (conn.getResponseCode() == 200) {
                data = readInputStream(is);
            } else {
                data = null;
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    // 关闭流
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 关闭连接
            conn.disconnect();
        }
        return data;
    }

    /**
     * 将流转换为字节
     *
     * @param is
     * @return
     */
    public static byte[] readInputStream(InputStream is) {
        /**
         * 捕获内存缓冲区的数据,转换成字节数组
         * ByteArrayOutputStream类是在创建它的实例时,程序内部创建一个byte型别数组的缓冲区,然后利用ByteArrayOutputStream和ByteArrayInputStream的实例向数组中写入或读出byte型数据。
         * 在网络传输中我们往往要传输很多变量,我们可以利用ByteArrayOutputStream把所有的变量收集到一起,然后一次性把数据发送出去。
         */
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 创建字节数组 1024 = 1M
        byte[] buffer = new byte[1024];
        // 防止无限循环
        int length = -1;
        try {
            // 循环写入数据到字节数组
            while ((length = is.read(buffer)) != -1) {
                baos.write(buffer, 0, length);
            }
            // 强制刷新,扫尾工作,主要是为了,让数据流在管道的传输工程中全部传输过去,防止丢失数据
            baos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 字节流转换字节数组
        byte[] data = baos.toByteArray();
        try {
            // 关闭读取流
            is.close();
            // 关闭写入流
            baos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return data;
    }

    /**
     * 获取本地图片的字节数
     *
     * @param imgPath
     * @return
     */
    public static String pathSize(String imgPath) {
        File file = new File(imgPath);
        FileInputStream fis;
        int fileLen = 0;
        try {
            fis = new FileInputStream(file);
            fileLen = fis.available();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bytes2kb(fileLen);
    }

    /**
     * 将获取到的字节数转换为KB,MB模式
     *
     * @param bytes
     * @return
     */
    public static String bytes2kb(long bytes) {
        BigDecimal filesize = new BigDecimal(bytes);
//        BigDecimal megabyte = new BigDecimal(1024 * 1024);
//        float returnValue = filesize.divide(megabyte, 2, BigDecimal.ROUND_UP).floatValue();
//        if (returnValue > 1)
//            return (returnValue + "MB");
//        BigDecimal kilobyte = new BigDecimal(1024);
//        returnValue = filesize.divide(kilobyte, 2, BigDecimal.ROUND_UP).floatValue();
//        return (returnValue + "KB");
        return filesize.toString();
    }



    public static String getImageFormat(String imagePath) throws IOException {
        // 字节数组
        byte[] data = null;
        String format = null;
        // 输入流
        InputStream is = null;
        // Http连接对象
        HttpURLConnection conn = null;
        ImageInputStream imageInputStream = null;
        try {
            // Url对象
            URL url = new URL(imagePath);
            // 打开连接
            conn = (HttpURLConnection) url.openConnection();
            // 打开读取 写入是setDoOutput
//	        conn.setDoOutput(true);
            conn.setDoInput(true);
            // 设置请求方式
            conn.setRequestMethod("GET");
            // 设置超时时间
            conn.setConnectTimeout(6000);
            // 得到访问的数据流
            is = conn.getInputStream();

            // 验证访问状态是否是200 正常
            if (conn.getResponseCode() == 200) {
                imageInputStream = ImageIO.createImageInputStream(is);
                ImageReader imageReader = ImageIO.getImageReaders(imageInputStream).next();
                format = imageReader.getFormatName();
            } else {
                format = null;
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    // 关闭流
                    is.close();
                }
                if(imageInputStream != null){
                    imageInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 关闭连接
            conn.disconnect();
        }
        return format;
    }



    /**
     * 将inputStream转化为file
     * @param is
     * @param file 要输出的文件目录
     */
    public static void inputStream2File(InputStream is, File file) throws IOException {
        OutputStream os = null;
        try {
            os = new FileOutputStream(file);
            int len = 0;
            byte[] buffer = new byte[8192];

            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
        } finally {
            os.close();
            is.close();
        }
    }

    /**
     * 获取视频详情
     * @param file
     * @return
     */
    public static VideoInfoVO getVideoInfo(String file) {
        VideoInfoVO videoInfoVO = new VideoInfoVO();
        FFmpegFrameGrabber grabber = null;
        try {
            grabber = FFmpegFrameGrabber.createDefault(file);
            // 启动 FFmpeg
            grabber.start();

            // 读取视频帧数
            videoInfoVO.setLengthInFrames(grabber.getLengthInVideoFrames());

            // 读取视频帧率
            videoInfoVO.setFrameRate(grabber.getVideoFrameRate());

            // 读取视频秒数
            videoInfoVO.setDuration(grabber.getLengthInTime() / 1000000.00);

            // 读取视频宽度
            videoInfoVO.setWidth(grabber.getImageWidth());

            // 读取视频高度
            videoInfoVO.setHeight(grabber.getImageHeight());


            videoInfoVO.setAudioChannel(grabber.getAudioChannels());

            videoInfoVO.setVideoCode(grabber.getVideoCodecName());

            videoInfoVO.setAudioCode(grabber.getAudioCodecName());
            // String md5 = MD5Util.getMD5ByInputStream(new FileInputStream(file));

            videoInfoVO.setSampleRate(grabber.getSampleRate());
            return videoInfoVO;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (grabber != null) {
                    // 此处代码非常重要,如果没有,可能造成 FFmpeg 无法关闭
                    grabber.stop();
                    grabber.release();
                }
            } catch (FFmpegFrameGrabber.Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 截取视频获得指定帧的图片(截取视频帧封面)
     *
     * @param video   源视频文件
     */
    public void getVideoPic(InputStream video) {
        FFmpegFrameGrabber ff = new FFmpegFrameGrabber(video);
        try {
            ff.start();

            // 截取中间帧图片(具体依实际情况而定)
            int i = 0;
            int length = ff.getLengthInFrames();
//            int middleFrame = length / 2;
            int middleFrame = 5;
            Frame frame = null;
            while (i < length) {
                frame = ff.grabFrame();
                if ((i > middleFrame) && (frame.image != null)) {
                    break;
                }
                i++;
            }

            // 截取的帧图片
            Java2DFrameConverter converter = new Java2DFrameConverter();
            BufferedImage srcImage = converter.getBufferedImage(frame);
            int srcImageWidth = srcImage.getWidth();
            int srcImageHeight = srcImage.getHeight();
            BufferedImage image = new BufferedImage(srcImageWidth, srcImageHeight, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = image.createGraphics();
            g2d.setColor(Color.RED);
            g2d.fillRect(0, 0, srcImageWidth, srcImageHeight);
            g2d.setColor(Color.WHITE);
            g2d.drawString("Hello, World!", 50, 100);
            g2d.dispose();
            File file = new File("image.png");
            try {
                ImageIO.write(image, "png", file);
            } catch (IOException e) {
                e.printStackTrace();
            }
            ff.stop();
        } catch (java.lang.Exception e) {
            e.printStackTrace();
            System.out.println(e);
        }
    }

    /**
     * 获取文件大小
     * @param inputStream
     * @return
     */
    public String getFileSize(InputStream inputStream){
        FileChannel fc = null;
        String size = "0";
        try {
            FileInputStream fis = convertToFileInputStream(inputStream);
            fc = fis.getChannel();
            BigDecimal fileSize = new BigDecimal(fc.size());
            size = String.valueOf(fileSize);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != fc) {
                try {
                    fc.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return size;
    }
    /**
     * inputStream转FileInputStream
     * @param inputStream
     * @return
     * @throws IOException
     */
    public static FileInputStream convertToFileInputStream(InputStream inputStream) throws IOException {
        File tempFile = File.createTempFile("temp", ".tmp");
        tempFile.deleteOnExit();
        try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }
        return new FileInputStream(tempFile);
    }

    public void start(Stage primaryStage) {
        String videoPath = "C:\\Users\\DELL\\Desktop\\test.mp4";
        double position = 1200.0; // 播放位置(以秒为单位)
        // 创建媒体播放器
        Media media = new Media(new File(videoPath).toURI().toString());
        MediaPlayer mediaPlayer = new MediaPlayer(media);
        mediaPlayer.setOnReady(() -> {
            mediaPlayer.seek(mediaPlayer.getTotalDuration().multiply(position / mediaPlayer.getTotalDuration().toSeconds()));
            mediaPlayer.play();
        });
        mediaPlayer.setAutoPlay(true);
        // 创建媒体视图
        MediaView mediaView = new MediaView(mediaPlayer);
        // 创建控制按钮
        Button playButton = new Button("播放");
        Button pauseButton = new Button("暂停");
        Button stopButton = new Button("停止");
        // 播放按钮点击事件
        playButton.setOnAction(event -> mediaPlayer.play());
        // 暂停按钮点击事件
        pauseButton.setOnAction(event -> mediaPlayer.pause());
        // 停止按钮点击事件
        stopButton.setOnAction(event -> mediaPlayer.stop());
        // 创建音量调节滑块
        Slider volumeSlider = new Slider(0, 1, 0.5);
        volumeSlider.setPrefWidth(100);
        mediaPlayer.volumeProperty().bind(volumeSlider.valueProperty());
        // 创建按钮布局
        HBox buttonBox = new HBox(10);
        buttonBox.setAlignment(Pos.CENTER);
        buttonBox.getChildren().addAll(playButton, pauseButton, stopButton);
        // 创建根布局
        BorderPane root = new BorderPane();
        root.setCenter(mediaView);
        root.setBottom(volumeSlider);
        root.setTop(buttonBox);
        // 创建场景
        Scene scene = new Scene(root, 800, 600);
        // 设置舞台
        primaryStage.setScene(scene);
        primaryStage.setTitle("Video Player");
        primaryStage.show();

    }
}

实体VideoInfoVO类:
package com.example.mybatisdemo02.model;


import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class VideoInfoVO {
    /**
     * 总帧数
     **/
    private int lengthInFrames;

    /**
     * 帧率
     **/
    private double frameRate;

    /**
     * 时长
     **/
    private double duration;

    /**
     * 视频编码
     */
    private String videoCode;
    /**
     * 音频编码
     */
    private String audioCode;

    private int width;
    private int height;
    private int audioChannel;
    private String md5;
    /**
     * 音频采样率
     */
    private Integer sampleRate;
    private Double fileSize;

    public String toJson() {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.writeValueAsString(this);
        } catch (Exception e) {
            return "";
        }
    }
}

调用:

public class Main {
    public static void main(String[] args) {
        VideoInfoVO videoInfoVO = FFmpegUtil.getVideoInfo("C:\\Users\\DELL\\Desktop\\test.mp4");
        int date = videoInfoVO.getLengthInFrames();
        double shichang = videoInfoVO.getDuration();
        System.out.println("帧数:"+date+"时长:"+shichang);
    }
}

结果如下图:

欢迎给个关注:

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

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

相关文章

day4|gin的中间件和路由分组

中间件其实是一个方法&#xff0c; 在.use就可以调用中间件函数 r : gin.Default()v1 : r.Group("v1")//v1 : r.Group("v1").Use()v1.GET("test", func(c *gin.Context) {fmt.Println("get into the test")c.JSON(200, gin.H{"…

vue项目入门——index.html和App.vue

vue项目中的index.html文件 在Vue项目中&#xff0c;index.html文件通常作为项目的入口文件&#xff0c;它包含了Vue应用程序的基础结构和配置。 该文件的主要作用是引入Vue框架和其他必要的库&#xff0c;以及定义Vue应用程序的启动配置。 import Vue from vue import App …

论文浅尝 | cTBLS:使用对话表格增强大型语言模型

笔记整理&#xff1a;金日辉&#xff0c;东南大学硕士&#xff0c;研究方向为表格模态数据相关的处理任务 链接&#xff1a;https://arxiv.org/pdf/2303.12024.pdf 1、动机 让人工智能对话具备多模态能力&#xff0c;可以拓宽人类与此类系统的对话范围。多模态会话人工智能面临…

【Linux】查看某个进程的tcp全连接队列长度

TCP三次握手成功后,会把连接放到全连接队列里,等待服务器端accept后移除。 如下图所示,图片转自:https://zhuanlan.zhihu.com/p/547279481 下图转自博客:https://zhuanlan.zhihu.com/p/340016138 TCP三次握手过程中,第一次握手server收到client的syn后,内核会把该连接存…

面向C++程序员的Rust教程(二)

先序文章请看&#xff1a; 面向C程序员的Rust教程&#xff08;一&#xff09; 所有权与移动语义 要说Rust语言跟其他语言最大的区别&#xff0c;那笔者觉得非数这个所有权和移动语义莫属。 深浅复制 对于绝大多数语言来说&#xff0c;变量/对象之间的赋值通常都是复制语义。…

Spring中BeanFactoryPostProcessor详解

目录 功能与作用 使用案例 spring提供的常见BeanFactoryPostProcessor 1.EventListenerMethodProcessor 2.BeanDefinitionRegistryPostProcessor 功能与作用 使用案例 spring提供的唯一BeanDefinitionRegistryPostProcessor 总结 功能与作用 参考BeanFactoryPostProce…

Kaggle:收入分类

先看一下数据的统计信息 import pandas as pd # 加载数据&#xff08;保留原路径&#xff0c;但在实际应用中建议使用相对路径或环境变量&#xff09; data pd.read_csv(r"C:\Users\11794\Desktop\收入分类\training.csv", encodingutf-8, encoding_errorsrepl…

蓝桥杯练习笔记(十六)

蓝桥杯练习笔记&#xff08;十六&#xff09; 一、 输入示例&#xff1a; 3 1 2 1 11 3 4 74 5 3这是用到了m叉树的结论&#xff1a;对于某个m叉树的一个节点n&#xff0c;假如其有完整子树&#xff0c;则其左子节点l为l(n-1)m2&#xff0c;右子节点r为rmn1。基于此我们可以快…

SpringBoot+thymeleaf完成视频记忆播放功能

一、背景 1)客户要做一个视频播放功能,要求是系统能够记录观看人员在看视频时能够记录看到了哪个位置,在下次观看视频的时候能够从该位置进行播放。 2)同时,也要能够记录是谁看了视频,看了百分之多少。 说明:由于时间关系和篇幅原因,我们这里只先讨论第一个要求,第…

如何使用极狐GitLab 启用自动备份功能

本文作者&#xff1a;徐晓伟 GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 本文主要讲述了如何极狐GitLab 自…

【面试八股总结】传输控制协议TCP(三)

参考资料 &#xff1a;小林Coding、阿秀、代码随想录 一、TCP拥塞控制⭐ 1. 慢启动 – Slow Start 慢启动是指TCP连接刚建立&#xff0c;一点一点地提速&#xff0c;试探一下网络的承受能力&#xff0c;以免直接扰乱了网络通道的秩序。 慢启动算法&#xff1a; 初始拥塞窗口…

IDEA中连接SQLserver数据库(DataGrip相同连接)

IDEA中连接SQLserver数据库(DataGrip相同连接) 1. 打开IDEA-database组件 2. 新建SQL server连接 3. 填写信息进行连接 填写连接名称&#xff0c;连接主机IP&#xff0c;端口&#xff0c;默认端口1433&#xff0c;数据库用户名密码&#xff0c;默认数据库用户名是sa 第一次连接…

CMakeLists.txt编写简单介绍:CMakeLists.txt同时编译.cpp和.cu

关于CMakeLists.txt的相关介绍,这里不赘诉,本人的出发点是借助于CMakeLists.txt掌握基本的C++构建项目流程,下面是本人根据网络资料以及个人实践掌握的资料。 CMakeList.txt构建C++项目 下图是一个使用CUDA实现hello world的项目,一般来说,一个标准的C++项目包括三个文件…

nginx | nginx反向代理/负载均衡/缓存

文章目录 一、Nginx 反向代理1.1 nginx 文件结构1.2 默认的nginx配置文件1.3 实践中的 nginx.conf 二、Nginx 负载均衡2.1 热备负载均衡2.2 轮询负责均衡2.3 加权轮询负载规则2.4 ip_hash 负载均衡2.5 对特定资源实现负载均衡2.6 对不同域名实现负载均衡2.7 实现带有URL重写的负…

探索 ZKFair 的Dargon Slayer蓝图,解锁新阶段的潜力

在当前区块链技术的发展中&#xff0c;Layer 2&#xff08;L2&#xff09;解决方案已成为提高区块链扩容性、降低交易成本和提升交易速度的关键技术&#xff0c;但它仍面临一些关键问题和挑战&#xff0c;例如用户体验的改进、跨链互操作性、安全性以及去中心化程度。在这些背景…

【已解决】Error: error:0308010C:digital envelope routines::unsupported

前言 场景&#x1f3ac; 使用 Ant Design &#xff0c; 执行 npm run dev 出现异常。 文章目录 前言场景&#x1f3ac; 异常信息解决方案方案一(推荐)MAC | Linux 电脑成功⬇️ Windows 电脑 方案2&#xff1a; 不懂留言 JavaPub 异常信息 我直接异常信息&#xff0c;你可以…

关于C#操作SQLite数据库的一些函数封装

主要功能&#xff1a;增删改查、自定义SQL执行、批量执行&#xff08;事务&#xff09;、防SQL注入、异常处理 1.NuGet中安装System.Data.SQLite 2.SQLiteHelper的封装&#xff1a; using System; using System.Collections.Generic; using System.Data.SQLite; using System.…

Linux提权!!!

上一篇文章讲了Windows的提权&#xff0c;那么这篇文章就来讲一下Linux的提权 1.SUID提权 suid权限 作用&#xff1a;让普通用户临时拥有该文件的属主的执行权限&#xff0c;suid权限只能应用在二进制可执行文件&#xff08;命令&#xff09;上&#xff0c;而且suid权限只能设置…

【前端面试3+1】09 ES6新特性、Promise原理 、浏览器从输入到页面渲染的过程、【罗马数字转整数】

一、ES6新特性 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript的一个重要更新版本&#xff0c;引入了许多新特性和语法改进&#xff0c;下面详细说明一些主要的新特性&#xff1a; 1. let 和 const 声明&#xff1a; 使用let和const关键字可以声明块级作用域的变量&a…

Go-Gin中优雅的实现参数校验,自定义错误消息提示

问题描述 在参数校验的时候我们一般会基于"github.com/go-playground/validator/v10"这个库给结构体加标签实现校验参数&#xff0c;当参数校验错误的时候&#xff0c;他的提示一般是英文的&#xff0c;怎么自定义参数错误提示呢&#xff1f;跟着我一步步来 注册校…