Windows下Rust编码实现MP4点播服务器

Rust编码可以实现众多简洁、可靠、高效的应用,但语法逻辑要求严格,尤其是依赖库的选择调用,需要耐心坚持“推敲”。借助DeepSeek并反复编程调试和问答改进,可以最终得到完整有效的Rust编码。下面分享Windows下Rust编码实现MP4点播服务器设计实现。

1 整体规划设计

1.1 功能分析设计

简单MP4点播服务器,设计简单html页面进行播放文件的选择和播放控制。

Rust后端编码设计,采用简易mp4库,不选用FFmpeg,避免复杂的环境配置和开发过程中的过多“坑”险。

项目整体规划如下:

  • Rust后端:处理MP4文件读取和HTTP服务
  • 前端HTML页面:远程选择播放文件和控制播放
  • 轻量级架构:使用高效Rust库实现核心功能
  • 文中文件名称的前后端支持

1.2 初始项目构建

把设想和功能要求,抛给AI-LLM工具,如腾迅的ima,借助内置的Hunyuan/DeepSeek,获取初始的项目构造和必须的文件编码,减轻初步完全编码的困难。以此为参照,开始后续项目架构和编码设计。关键的提问描述如下:

请为初学者给出Windows下Rust编码的简单MP4点播服务器,设计简单html页面进行远程选择播放文件并进行播放控制。不用FFMPEG。给出项目文件结构和完整编码。

图1是腾迅ima的运用组合截图。

图1 腾迅ima项目借力的运用组合截图

2 项目构建准备

2.1 项目整体构建

适应Windows操作系统,选择MinGW-GNU支持的Rust底层支持环境。简化简洁安装,避免过多且易于冲突调整,这里采用离线版:rust-1.88.0-x86_64-pc-windows-gnu.msi,一步到位。

进而安装采用RustRoverIDE集成开发环境,执照上述ima的设计指导,构建整个项目工程,框架结构如下文本框所示。

mp4-server/

├── Cargo.toml            # 项目依赖配置

├── videos/               # 存放MP4视频文件的目录

├── static/                # 前端静态文件

│   └── index.html             # 交互操控界面

└── src/

    ├── main.rs                # 服务器入口

    ├── handlers.rs             # HTTP请求处理

    └── mp4_utils.rs            # MP4文件处理工具

2.2 项目依赖管理

添加项目所需依赖--Cargo.toml依赖配置

[package]name = "mp4_server2"version = "0.1.0"edition = "2024"[dependencies]warp = "0.3"                                   # HTTP服务器框架

tokio = { version = "1", features = ["full"] }          # 异步运行时

serde = { version = "1.0", features = ["derive"] }      # 序列化

serde_json = "1.0"                              # JSON处理

mp4 = "0.14.0"                                 # MP4文件处理库

futures = "0.3"                                  # 异步支持

lazy_static = "1.4"regex = "1.11.1"                                # 静态初始化

encoding = "0.2.33"percent-encoding = "2.3.1"tokio-util = "0.7.16"hyper = "0.14.32"                               # 支持GBK/GB18030解码

3 后端编码实现

3.1 Mp4文件处理

src/mp4_utils.rs

use mp4::{Mp4Reader, Result}; use std::fs::File;

use std::io::{BufReader, Read, Seek, SeekFrom}; use std::path::Path;

// 获取视频文件元数据

pub fn get_video_metadata(path: &Path) -> Result<(u64, u32, u32)> {

    let file = File::open(path)?;

    let size = file.metadata()?.len();

    let reader = BufReader::new(file);

    let mp4 = Mp4Reader::read_header(reader, size)?;

    let duration = mp4.duration().as_secs();

    let width = mp4.tracks().values().next().map(|t| t.width()).unwrap_or(0);

    let height = mp4.tracks().values().next().map(|t| t.height()).unwrap_or(0);

    Ok((duration, width as u32, height as u32))

}

// 流式传输视频文件

pub fn stream_video_file(path: &Path, start: u64, end: Option<u64>) -> std::io::Result<Vec<u8>> {

    let mut file = File::open(path)?;

    let file_size = file.metadata()?.len();

    // 计算实际读取范围

    let end = end.unwrap_or(file_size); let length = end - start;

    // 定位并读取数据

    file.seek(SeekFrom::Start(start))?;

    let mut buffer = vec![0; length as usize];

    file.read_exact(&mut buffer)?;

    Ok(buffer)

}

3.2 Http请求处理

src/handlers.rs

use warp::{Rejection, Reply};

use std::path::{PathBuf}; use std::fs; use std::ffi::OsString;

use encoding::{all::GB18030, EncoderTrap, Encoding};

use percent_encoding::{percent_decode, utf8_percent_encode, NON_ALPHANUMERIC};

use serde::Serialize; use tokio_util::io::ReaderStream; use hyper::Body; use crate::mp4_utils;

#[derive(Serialize)]

struct VideoInfo { name: String, duration: u64, width: u32, height: u32, }

// 获取视频列表(兼容中文文件名)

pub async fn list_videos() -> Result<impl Reply, Rejection> {

    let videos_dir = PathBuf::from("./videos"); let mut videos = Vec::new();

    if let Ok(entries) = fs::read_dir(videos_dir) {

        for entry in entries.flatten() {

            let path = entry.path();

            if path.is_file() && path.extension().map_or(false, |ext| ext == "mp4") {

                // 使用OsString原生处理文件名(兼容GBK/UTF-8

                let os_name: OsString = entry.file_name();

                let name = os_name.to_string_lossy().into_owned(); // 转换为UTF-8字符串

                if let Ok((duration, width, height)) = mp4_utils::get_video_metadata(&path) {

                    videos.push(VideoInfo { name, duration, width, height });

                }

            }

        }

    }

    Ok(warp::reply::json(&videos))

}

// 流式传输视频

pub async fn stream_video(name: String, range: Option<String>) -> Result<impl Reply, Rejection> {

    // 1. URL解码文件名(前端encodeURIComponent的逆操作)

    let decoded_name = percent_decode(name.as_bytes())

        .decode_utf8().map_err(|_| warp::reject::not_found())?

        .into_owned();

    // 2. UTF-8转换为GBKWindows原生编码)

    let _gbk_bytes = GB18030.encode(&decoded_name, EncoderTrap::Strict)

        .map_err(|_| warp::reject::not_found())?;

    // 3. 通过OsString构建路径(关键修复)

    // 使用OsString::from而不是from_vec

    let os_name = OsString::from(decoded_name.clone());

    let path = PathBuf::from("./videos").join(os_name);

    // 4. 解析Range

    let (start, end) = if let Some(range_header) = range {

        parse_range_header(&range_header, &path)?

    } else {

        (0, None)

    };

    // 5. 异步读取文件

    let file = tokio::fs::File::open(&path).await

        .map_err(|_| warp::reject::not_found())?;

    // 6. 创建流式响应体

    let stream = ReaderStream::new(file); let body = Body::wrap_stream(stream);

    // 7. 获取文件元数据

    let metadata = tokio::fs::metadata(&path).await.map_err(|_| warp::reject::not_found())?;

    let file_size = metadata.len();

    // 8. 构建Content-Range

    let content_range = if let Some(end) = end {

        let end = end.min(file_size - 1);

        format!("bytes {}-{}/{}", start, end, file_size)

    } else {

        format!("bytes {}-{}/{}", start, file_size - 1, file_size)

    };

    // 9. 设置响应头(解决浏览器中文乱码)

    let safe_name = utf8_percent_encode(&decoded_name, NON_ALPHANUMERIC).to_string();

    let content_disposition = format!(

        "attachment; filename=\"{}\"; filename*=utf-8''{}", safe_name, safe_name );

    // 10. 构建响应,使用warp::reply::Response包装Body

    let mut response = warp::reply::Response::new(body);

    let headers = response.headers_mut();

    headers.insert("Content-Type", "video/mp4".parse().unwrap());

    headers.insert("Content-Range", content_range.parse().unwrap());

    headers.insert("Content-Disposition", content_disposition.parse().unwrap());

    Ok(response)

}

// 解析Range

fn parse_range_header(range: &str, path: &PathBuf) -> Result<(u64, Option<u64>), Rejection> {

    let file_size = fs::metadata(path).map_err(|_| warp::reject::not_found())?.len();

    if let Some(captures) = regex::Regex::new(r"bytes=(\d+)-(\d*)").unwrap()captures(range) {

        let start = captures.get(1).unwrap().as_str().parse::<u64>()

            .map_err(|_| warp::reject::not_found())?;

        let end = captures.get(2).unwrap().as_str();

        let end = if end.is_empty() {

            None

        } else {

            Some(end.parse::<u64>().map_err(|_| warp::reject::not_found())?.min(file_size - 1))

        };

        return Ok((start, end));

    }

    Err(warp::reject::not_found())

}

// 辅助函数:URL编码转换(供前端使用)

pub fn encode_filename(name: &str) -> String {

    utf8_percent_encode(name, NON_ALPHANUMERIC).to_string()

}

3.3 服务器主入口

src/main.rs

mod handlers; mod mp4_utils;

use warp::Filter; use handlers::{list_videos, stream_video};

#[tokio::main]

async fn main() {

    // 创建路由

    let list_route = warp::path!("api" / "videos").and(warp::get()).and_then(list_videos);

    let stream_route = warp::path!("api" / "videos" / String)

        .and(warp::get()).and(warp::header::optional("Range")).and_then(stream_video);

    let static_files = warp::path("static").and(warp::fs::dir("./static"));

    let index = warp::path::end().and(warp::fs::file("./static/index.html"));

    // 组合所有路由

    let routes = list_route.or(stream_route).or(static_files).or(index)

        .with(warp::cors().allow_any_origin());

    // 启动服务器

    println!("Server started at http://localhost:9700");

    warp::serve(routes).run(([0, 0, 0, 0], 9700)).await;

}

4 前端交互页面设计

static/index.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>MP4视频服务器</title>

  <style>

    :root { --primary-color: #3498db; --secondary-color: #2980b9;

      --background-color: #f5f7fa; --card-bg: #ffffff; --text-color: #333333;

      --border-color: #e0e0e0; --success-color: #2ecc71; --hover-color: #f1f9ff; }

    * { margin: 0; padding: 0; box-sizing: border-box;

      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }

    body { background-color: var(--background-color);

      color: var(--text-color); line-height: 1.6; padding: 2px; }

    .container { max-width: 1200px; margin: 0 auto; display: grid;

      grid-template-columns: 1fr 2fr; gap: 10px; }

    @media (max-width: 768px) { .container { grid-template-columns: 1fr; } }

    header { grid-column: 1 / -1; text-align: center; margin-bottom: 2px;

      padding: 20px 0; border-bottom: 1px solid var(--border-color); }

    h1 { color: var(--primary-color); font-size: 2.5rem; margin-bottom: 1px; }

    .card { background: var(--card-bg); border-radius: 10px; padding: 25px;

      box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); transition: transform 0.3s ease; }

    .card:hover { transform: translateY(-5px); }

    .video-list-container { height: 100%; display: flex; flex-direction: column; }

    .video-list-header { display: flex; justify-content: space-between;

      align-items: center; margin-bottom: 15px; padding-bottom: 10px;

      border-bottom: 1px solid var(--border-color); }

    .video-list-header h2 { font-size: 1.5rem; color: var(--primary-color); }

    .video-count { background: var(--primary-color); color: white;

      border-radius: 20px; padding: 2px 10px; font-size: 0.9rem; }

    #videoList { list-style: none; max-height: 500px; overflow-y: auto;

      flex-grow: 1; border: 1px solid var(--border-color);

      border-radius: 8px; padding: 5px; }

    #videoList::-webkit-scrollbar { width: 8px; }

    #videoList::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 4px; }

    #videoList::-webkit-scrollbar-thumb { background: var(--primary-color); border-radius: 4px; }

    #videoList li { padding: 12px 15px; border-bottom: 1px solid var(--border-color);

      cursor: pointer; transition: background-color 0.2s; display: flex;

      justify-content: space-between; align-items: center; }

    #videoList li:last-child { border-bottom: none; }

    #videoList li:hover { background-color: var(--hover-color); }

    #videoList li.active { background-color: var(--primary-color); color: white; }

    .video-duration { background: var(--secondary-color); color: white;

      border-radius: 4px; padding: 2px 8px; font-size: 0.85rem; }

    .player-container { display: flex; flex-direction: column; gap: 20px; }

    .video-player-wrapper { position: relative; padding-bottom: 56.25%; /* 16:9 aspect ratio */

      height: 0; overflow: hidden; border-radius: 8px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); }

    #videoPlayer { position: absolute; top: 0; left: 0; width: 100%; height: 100%;background: #000; }

    .controls { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; background:var(--card-bg);

      padding: 15px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); }

    .btn { background: var(--primary-color); color: white; border: none; padding: 10px 15px;

      border-radius: 5px; cursor: pointer; transition: background 0.3s;

      font-weight: 600; display: flex; align-items: center; gap: 5px; }

    .btn:hover { background: var(--secondary-color); }

    .btn i { font-size: 1.2rem; }

    .seek-container { flex-grow: 1; display: flex; align-items: center; gap: 10px;

    #seekBar { flex-grow: 1; height: 8px; -webkit-appearance: none;

      background: #e0e0e0; border-radius: 4px; outline: none; }

    #seekBar::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px;

      background: var(--primary-color); border-radius: 50%; cursor: pointer; }

    .time-display { font-size: 0.9rem; color: #666; min-width: 80px; text-align: center; }

    .search-container { margin-top: 15px; position: relative; }

    #searchInput { width: 100%; padding: 10px 15px 10px 40px;

      border: 1px solid var(--border-color); border-radius: 5px; font-size: 1rem; }

    .search-icon { position: absolute; left: 15px; top: 50%; transform: translateY(-50%); color: #777; }

    .empty-state { text-align: center; padding: 30px; color: #777; }

    .empty-state i { font-size: 3rem; margin-bottom: 15px; color: #ddd; }

    .video-info { display: flex; justify-content: space-between;

      font-size: 0.9rem; color: #666; margin-top: 5px; }

  </style>

  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">

</head>

<body>

<div class="container">

  <header>

    <h1><i class="fas fa-play-circle"></i>MP4视频服务器</h1>

    <p>流式传输管理MP4视频流,恺肇乾,2025-08-06</p>

  </header>

  <div class="video-list-container card">

    <div class="video-list-header">

      <h2>有效视频数</h2>

      <span class="video-count">12 videos</span>

    </div>

    <div class="search-container">

      <i class="fas fa-search search-icon"></i>

      <input type="text" id="searchInput" placeholder="搜索视频...">

    </div>

    <ul id="videoList">

      <!-- Videos will be populated by JavaScript -->

      <li>

        <span>Introduction to Rust Programming</span>

        <span class="video-duration">10:24</span>

      </li>

      <li class="active">

        <span>Building Web Servers with Rust</span>

        <span class="video-duration">15:42</span>

      </li>

      <li>

        <span>Concurrency in Rust Explained</span>

        <span class="video-duration">22:18</span>

      </li>

      <li>

        <span>Rust vs C++ Performance Comparison</span>

        <span class="video-duration">18:05</span>

      </li>

      <li>

        <span>Memory Management in Rust</span>

        <span class="video-duration">12:37</span>

      </li>

      <li>

        <span>Creating CLI Tools with Rust</span>

        <span class="video-duration">14:56</span>

      </li>

      <li>

        <span>WebAssembly with Rust Tutorial</span>

        <span class="video-duration">25:12</span>

      </li>

      <li>

        <span>Async Programming in Rust</span>

        <span class="video-duration">20:45</span>

      </li>

      <li>

        <span>Rust for Embedded Systems</span>

        <span class="video-duration">16:33</span>

      </li>

      <li>

        <span>Testing Strategies in Rust</span>

        <span class="video-duration">11:29</span>

      </li>

      <li>

        <span>Building REST APIs with Actix</span>

        <span class="video-duration">19:17</span>

      </li>

      <li>

        <span>Rust Macros Deep Dive</span>

        <span class="video-duration">17:48</span>

      </li>

    </ul>

  </div>

  <div class="player-container">

    <div class="card">

      <div class="video-player-wrapper">

        <video id="videoPlayer" controls poster="https://picsum.photos/800/450?random">

          <source src="" type="video/mp4">Your browser does not support the video tag.

        </video>

      </div>

    </div>

    <div class="controls card">

      <button id="playBtn" class="btn"><i class="fas fa-play"></i>播放</button>

      <button id="pauseBtn" class="btn"><i class="fas fa-pause"></i>暂停</button>

      <div class="seek-container">

        <span class="time-display" id="currentTime">0:00</span>

        <input type="range" id="seekBar" min="0" max="100" value="0">

        <span class="time-display" id="duration">0:00</span>

      </div>

      <button id="fullscreenBtn" class="btn"><i class="fas fa-expand"></i></button>

    </div>

    <div class="card">

      <h3>现在正在播放: <span id="nowPlaying">Building Web Servers with Rust</span></h3>

      <div class="video-info">

        <span>时长: <span id="videoDuration">15:42</span></span>

        <span>分辩率: 1080p</span>

        <span>大小: 未定 MB</span>

      </div>

    </div>

  </div>

</div>

</body>

<script>

  // 获取视频列表

  async function loadVideos() {

    try {

      const response = await fetch('/api/videos');

      const videos = await response.json();

      const listElement = document.getElementById('videoList');

      const videoCount = document.querySelector('.video-count');

      // 清空列表(除了示例项)

      listElement.innerHTML = '';

      if (videos.length === 0) {

        listElement.innerHTML = `

            <div class="empty-state">

              <i class="fas fa-film"></i>

              <p>No videos found</p>

              <p>Upload videos to the server to get started</p>

            </div>

          `;

        videoCount.textContent = '0 ';

        return;

      }

      videoCount.textContent = `${videos.length} ${videos.length === 1 ? '' : ''}`;

      videos.forEach(video => {

        const li = document.createElement('li');

        // 格式化时间为 MM:SS

        const minutes = Math.floor(video.duration / 60);

        const seconds = video.duration % 60;

        const formattedTime = `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;

        li.innerHTML = `

            <span>${video.name}</span>

            <span class="video-duration">${formattedTime}</span>

          `;

        li.onclick = () => {

          // 移除所有active

          document.querySelectorAll('#videoList li').forEach(item => {

            item.classList.remove('active');

          });

          // 添加active类到当前项

          li.classList.add('active');

          playVideo(video.name);

        };

        listElement.appendChild(li);

      });

      // 默认选择第一个视频

      if (videos.length > 0) {

        listElement.firstChild.classList.add('active');

        playVideo(videos[0].name);

      }

    } catch (error) {

      console.error('Error loading videos:', error);

      const listElement = document.getElementById('videoList');

      listElement.innerHTML = `

          <div class="empty-state">

            <i class="fas fa-exclamation-triangle"></i>

            <p>Failed to load videos</p>

            <p>Please check your connection</p>

          </div>

        `;

    }

  }

  // 播放视频

  function playVideo(name) {

    const player = document.getElementById('videoPlayer');

    const nowPlaying = document.getElementById('nowPlaying');

    const encodedName = encodeURIComponent(name);

    nowPlaying.textContent = name;

    player.src = `/api/videos/${encodedName}`;

    player.load();

    // 更新视频信息

    updateVideoInfo(name);

  }

  // 更新视频信息(模拟)

  function updateVideoInfo(name) {

    const durationElement = document.getElementById('videoDuration');

    const videoItems = document.querySelectorAll('#videoList li');

    // 在实际应用中,这里会从API获取详细信息

    // 这里只是模拟从列表项中获取时长

    videoItems.forEach(item => {

      if (item.textContent.includes(name)) {

        const durationSpan = item.querySelector('.video-duration');

        if (durationSpan) {

          durationElement.textContent = durationSpan.textContent;

        }

      }

    });

  }

  // 搜索功能

  function setupSearch() {

    const searchInput = document.getElementById('searchInput');

    searchInput.addEventListener('input', function() {

      const searchTerm = this.value.toLowerCase();

      const videoItems = document.querySelectorAll('#videoList li');

      videoItems.forEach(item => {

        const videoName = item.querySelector('span:first-child').textContent.toLowerCase();

        if (videoName.includes(searchTerm)) {

          item.style.display = 'flex';

        } else {

          item.style.display = 'none';

        }

      });

    });

  }

  // 播放控制

  function setupPlayerControls() {

    const player = document.getElementById('videoPlayer');

    const playBtn = document.getElementById('playBtn');

    const pauseBtn = document.getElementById('pauseBtn');

    const seekBar = document.getElementById('seekBar');

    const currentTime = document.getElementById('currentTime');

    const duration = document.getElementById('duration');

    const fullscreenBtn = document.getElementById('fullscreenBtn');

    playBtn.addEventListener('click', () => {

      player.play();

    });

    pauseBtn.addEventListener('click', () => {

      player.pause();

    });

    // 更新进度条和时间显示

    player.addEventListener('timeupdate', () => {

      const value = (player.currentTime / player.duration) * 100;

      seekBar.value = isNaN(value) ? 0 : value;

      // 更新时间显示

      currentTime.textContent = formatTime(player.currentTime);

    });

    // 视频加载后更新总时长

    player.addEventListener('loadedmetadata', () => {

      duration.textContent = formatTime(player.duration);

    });

    seekBar.addEventListener('input', () => {

      const time = player.duration * (seekBar.value / 100);

      player.currentTime = time;

    });

    // 全屏功能

    fullscreenBtn.addEventListener('click', () => {

      if (player.requestFullscreen) {

        player.requestFullscreen();

      } else if (player.mozRequestFullScreen) {

        player.mozRequestFullScreen();

      } else if (player.webkitRequestFullscreen) {

        player.webkitRequestFullscreen();

      } else if (player.msRequestFullscreen) {

        player.msRequestFullscreen();

      }

    });

  }

  // 格式化时间为 MM:SS

  function formatTime(seconds) {

    const minutes = Math.floor(seconds / 60);

    const secs = Math.floor(seconds % 60);

    return `${minutes}:${secs < 10 ? '0' : ''}${secs}`;

  }

  // 初始化

  window.onload = function() {

    loadVideos();

    setupPlayerControls();

    setupSearch();

  };

</script>

</html>

5 测调试部署运行

5.1 使用说明

  1. 创建项目结构并添加上述文件
  2. 在项目根目录创建videos文件夹并放入MP4文件
  3. 运行服务器:cargo run
  4. 访问 http://localhost:9700

图2 浏览器交互运行及其跟踪调试组合窗口截图

5.2 IDE跟踪调试

RustRoverIDE跟踪调试组合窗口截图,如图3所示。

图3 RustRoverIDE跟踪调试组合窗口截图

5.3 功能特点

  1. MP4文件处理:使用mp4-rust库高效读取MP4文件
  2. 流式传输:支持HTTP Range请求,实现视频流式播放
  3. 元数据提取:自动获取视频时长和分辨率信息
  4. 响应式界面:简洁的前端界面支持视频选择和播放控制
  5. 轻量级架构:无外部依赖,仅使用Rust标准库和高效组件

5.4 部署运行

将生成的exe文件连同static、videos两个目录文件,一起放到服务器上,进行exe执行文件,即可远程通过浏览器交互访问了。

注意,生成exe文件时,主程序中的IP需是:“0,0,0,0”,不能再是“127.0.0.1”。

    warp::serve(routes).run(([0, 0, 0, 0], 9700)).await;

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

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

相关文章

ubuntu-相关指令

1、串口1.1确认在系统中检查设备是否正常加载&#xff0c;在终端输入以下命令&#xff1a;way1&#xff1a;ll /dev | grep ttyUSB&#xff08;ll是LL的小写&#xff09; way2&#xff1a;ll /dev | grep ttyACM way3&#xff1a;ll /dev | grep ttyCH343USB&#…

docker容器临时文件去除,服务器容量空间

概述&#xff1a; 接到告警提醒&#xff0c;服务器容量不足&#xff0c;去查看了一下&#xff0c;发现确实100g左右容量已基本用完&#xff1b;分析&#xff1a; 1&#xff09;查看根目录下哪些文件夹占用容量较大 使用命令“ du -ah --max-depth1 / ” 查看目标目录下所有文件…

损耗对信号质量的影响

损耗通常分为介质损耗与导体损耗&#xff1a;介质损耗&#xff1a;介质被施加电场后介质内部带电粒子在外加电场的作用力下进行微小移动介质损耗与频率成正比导体损耗&#xff1a;导体由于存在电阻&#xff0c;在有电流流过时产生的热量造成的损耗为导体损耗。同时&#xff0c;…

【42】【OpenCV C++】 计算图像某一列像素方差 或 某一行像素的方差;

文章目录1 要使用到的函数 和 原理1.1 cv::meanStdDev 函数详解——计算均值和标准差1 .2 方差的通俗解释2 代码实现3 问题3.1 入口参数const cv::Mat& img 和 const cv::Mat img区别项目要求&#xff1a;C OPenCV 中 图像img ,当 string ROIdirection “H”时&#xff0c;…

元图 CAD 插件化革命:突破效率瓶颈,重构智能协作新范式

在建筑、机械、机电等工程领域&#xff0c;传统CAD软件的功能固化与场景割裂已成为效率提升的瓶颈。设计师常面临“通用工具难适配专业需求”、“跨平台协作效率低下”、“数据孤岛阻碍创新”等痛点。元图CAD凭借“场景插件化“核心技术&#xff0c;以模块化能力突破行业桎梏&a…

T:归并排序

归并排序.逆序对简介.归并排序.习题.逆序对简介 \;\;\;\;\;\;\;\;简单介绍一下归并排序的原理&#xff0c;逆序对的基本概念&#xff0c;然后收集相关的练习。 直接用一个基础问题来引入。 因此知道了: \;\;\;\;\;\;\;\;逆序对就是一对数满足 i<j&&nums[i]>nu…

三极管三种基本放大电路:共射、共集、共基放大电路

文章目录一、共集放大电路1.静态分析2.动态分析二、共基放大电路1.静态分析2.动态分析总结如何判断共射、共集、共基放大电路&#xff1f; 电路的输入回路与输出回路以发射极为公共端的电路称为共射放大电路。 电路的输入回路与输出回路以集电极为公共端的电路称为共集放大电路…

Function AI 助力用户自主开发 MCP 服务,一键上云高效部署

作者&#xff1a;靖苏 在 AI 与云原生协同创新的浪潮下&#xff0c;多模型、多场景智能应用日益普及。开发者面临的首要挑战&#xff0c;是如何实现模型之间、服务之间的高效协同&#xff0c;以及如何便捷地将自主研发能力拓展到云端&#xff0c;形成灵活可扩展的智能服务。MC…

c++编译环境安装(gcc、cmake)

一、gcc下载 下载地址&#xff1a;https://ftp.gnu.org/gnu/gcc/ 选择想要下载的版本&#xff0c;然后解压&#xff0c;查看 contrib/download_prerequisites 中的依赖。 以我下载的 gcc-7.3.0 为例&#xff0c; 二、安装依赖包 【gmp】 https://ftp.gnu.org/gnu/gmp/ 【is…

基于贝叶斯的营销组合模型实战案例(PyMC实践)

文章出自&#xff1a;基于营销预算优化的媒体投入分配研究 本篇技术亮点在于结合了广告饱和度和累积效应&#xff0c;通过数学模型和数值优化方法&#xff0c;精确计算电视与数字媒体的最佳预算分配比例&#xff0c;实现增量销售最大化。该方法适合有多渠道广告投放需求、预算…

react_05create-react-app脚手架详细解析(export)

脚手架是什么&#xff1f; 是一种工具:快速生成项目的工程化结构&#xff0c;让项目从搭建到开发&#xff0c;到部署&#xff0c;整个流程变得快速和便捷。 安装过程: 1.安装node,安装完成后验证版本,出现对应版本就表示成功 node --version npm --version2.React脚手架默认是使…

Uncaught TypeError: Illegal invocation

报错信息Uncaught TypeError: Illegal invocation关键代码$.operate.post(prefix "/edit", { "taskId": taskId, "taskStatus": completed });<input id"taskId" style"display: none;">[[${completeTask.taskId}]]&…

深入解析Go设计模式:责任链模式实战

什么是责任链模式? 责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它通过构建处理者链来传递请求。每个处理者既能自行决定是否处理当前请求,也可将请求转交给后续处理者。该模式的核心优势在于解耦请求发送方与处理方,使多个对象都能获得处理请求的机…

机器视觉系统工业相机的成像原理及如何选型

机器视觉系统是一种模拟人类视觉功能&#xff0c;通过光学装置和非接触式传感器获取图像数据&#xff0c;并进行分析和处理&#xff0c;以实现对目标物体的识别、测量、检测和定位等功能的智能化系统。其目的是让机器能够理解和解释视觉信息&#xff0c;从而做出决策或执行任务…

Java如何快速实现短信登录?

全文目录&#xff1a;开篇语前言1. 短信登录的工作原理2. 短信登录的优点3. 短信登录的缺点4. 短信登录的实现示例&#xff1a;使用 Java 实现短信登录的流程4.1 发送短信验证码&#xff08;伪代码&#xff09;4.2 使用第三方短信平台发送短信&#xff08;以阿里云为例&#xf…

HTML已死,HTML万岁——重新思考DOM的底层设计理念

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

客户管理系统的详细项目框架结构

以下是针对客户管理系统的详细项目框架结构&#xff0c;整合了核心业务模块&#xff08;客户信息、合同管理、售前售后等&#xff09;&#xff0c;并补充了实用扩展模块&#xff08;如数据统计、标签管理等&#xff09;&#xff0c;严格遵循Django模块化设计原则&#xff1a; c…

【01】OpenCV C#——C#开发环境OpenCvSharp 环境配置 工程搭建 及代码测试

文章目录一、OpenCV 介绍二、OpenCvSharp 介绍三、OpenCvSharp环境搭建3.1 创建新项目3.2 添加 NuGet组件3.3 代码测试3.4 相较于 C OpenCV不同的之处四、LearnOpenCV有时候&#xff0c;单纯c#做前端时会联合C实现的dll来落地某些功能由于有时候会用C - Opencv实现算法后封装成…

【解决办法】报错Found dtype Long but expected Float

Found dtype Long but expected Float错误通常发生在尝试将一个数据类型为Long的张量传递给一个期望数据类型为Float的函数或操作时。在PyTorch中&#xff0c;Long和Float是两种常见的数据类型&#xff0c;分别对应于64位整数和32位浮点数。某些函数或操作可能只接受特定数据类…

QtC++ 调用 tesseract开源库 搭配 Opencv 实现文字识别:从tesseract库基本介绍到实际应用实现

前言 在当今数字化时代&#xff0c;文字识别&#xff08;OCR&#xff09;技术已经渗透到我们生活和工作的方方面面&#xff0c;从扫描文档的自动排版到车牌识别、票据信息提取等&#xff0c;都离不开 OCR 技术的支持。而在众多 OCR 实现方案中&#xff0c;QtC 结合 tesseract 和…