系统设计——大文件传输方案设计

摘要

大文件传输是指通过网络将体积较大的文件从一个位置发送到另一个位置的过程。这些文件可能包括高清视频、大型数据库、复杂的软件安装包等,它们的大小通常超过几百兆字节(MB)甚至达到几个吉字节(GB)或更大。大文件传输可能面临一些挑战,比如传输速度慢、网络不稳定导致的传输中断、以及存储空间的限制等。为了有效地传输大文件,可能需要使用特定的技术,比如压缩文件以减少传输数据量、使用高速网络连接、或者采用分块传输技术来提高传输的稳定性和效率。

1. 什么是大文件传输

大文件的定义取决于具体的应用场景、传输技术和硬件资源。通常情况下,大文件的定义与以下几个因素相关:

1.1. 大文件常见定义

网络传输中

  • >10MB:对低速网络(如移动网络或不稳定的无线连接)来说,10MB及以上的文件可能需要优化。
  • >100MB:在普通网络环境中,100MB以上的文件一般被认为是大文件,需要特殊优化,如分块或流式传输。
  • >1GB:在高速网络环境中,1GB及以上的文件通常被认为是大文件,传输时需要额外注意内存、带宽和传输错误恢复。

存储或操作中

  • >100MB:对于低配置设备(如嵌入式设备或老旧服务器),内存无法一次加载,文件需要分块处理。
  • >1GB:在文件处理场景中,1GB及以上的文件可能会对 I/O 性能或存储速度造成显著影响。

1.2. 从技术视角分析

大文件的判断标准与以下技术限制相关:

网络传输协议限制

  • HTTP:对于普通 HTTP 上传,传输 >100MB 文件可能会遇到超时问题,需要优化(如分片上传或断点续传)。
  • Socket:理论上无文件大小限制,但需要注意缓冲区大小设置和断点续传。
  • gRPC:默认单个消息限制为 4MB,可以通过配置增加限制,但传输大文件时推荐使用流式传输。

硬件和系统限制

  • 内存:如果文件无法一次性加载到内存中(如文件大小 > 系统内存的 50%),需要分块处理。
  • 文件系统:某些文件系统对单个文件大小有上限。例如,FAT32 的最大文件大小是 4GB。
  • 用户体验:对用户来说:传输文件超过 30秒 就可能被认为是“较大”文件。超过 1分钟 需要提供进度条或断点续传功能。

1.3. 实际场景中的大文件划分参考

文件大小

定义

应对策略

<10MB

小文件

一次性传输即可,无需特别优化。

10MB-100MB

中等大小文件

使用分块传输或流式传输。

100MB-1GB

大文件

必须分块,建议使用断点续传、校验完整性等优化手段。

>1GB

超大文件

分块、多线程传输,尽量使用高效协议(如 gRPC 或专用大文件传输工具)。

>10GB

超级大文件(少见)

考虑带外传输(如 FTP、SFTP)或直接通过硬盘快递等方式完成传输。

1.4. 判断是否为大文件条件参考

  1. 内存不足:如果程序无法一次性加载文件到内存,或者引发内存溢出错误(OutOfMemoryError)。
  2. 传输时间过长:文件传输时间明显超出用户期望(如 10 秒以上)。
  3. I/O 性能瓶颈:文件操作导致磁盘或网络 I/O 压力显著增加。
  4. 网络不稳定:传输过程频繁中断或出现丢包。

1.5. 针对大文件的优化技术参选

  1. 分块传输:将文件分为小块逐步传输。
  2. 断点续传:网络中断时无需重新传输已完成部分。
  3. 压缩传输:减少文件大小,加快传输速度。
  4. 流式传输:在文件读取和写入时流式处理,避免一次性加载到内存。
  5. 多线程并行传输:提高传输效率。
  6. 分片存储(Shard):对于超大文件,考虑存储时按逻辑拆分。

2. 大文件传输有什么挑战

大文件传输的挑战和问题主要来自于文件的体积、传输过程中的网络和硬件限制。以下是详细分析,以及常见问题和对应的解决方案。

2.1. 网络限制

  • 传输速度:文件越大,传输时间越长,尤其在低带宽或高延迟网络中表现明显。
  • 网络中断:大文件传输过程中,网络的不稳定(如超时、丢包)可能导致传输失败,需要重新开始。
  • 带宽占用:大文件传输可能占用大量带宽,影响其他任务的正常运行。
  • 跨网络传输:不同的网络环境(如企业内网与公网)可能有防火墙、代理或限速限制。

2.2. 系统与硬件限制

  • 内存不足:如果需要将大文件加载到内存中处理,可能导致内存溢出(OutOfMemoryError)。
  • 磁盘性能:大文件的读写操作对磁盘 I/O 是一个挑战,尤其是在磁盘性能较差或并发读写多的情况下。
  • 文件系统限制:某些文件系统对单个文件大小有上限(如 FAT32 的单文件大小限制为 4GB)。磁盘空间不足可能导致文件传输失败。

2.3. 数据完整性

  • 文件损坏:网络传输中的数据丢失或错误可能导致接收端文件不完整或损坏。
  • 校验困难:大文件的完整性校验(如 MD5、SHA-256)耗时较长。

2.4. 并发与多用户冲突

  • 多用户竞争资源:多个用户同时上传或下载大文件时,可能导致服务器资源不足(CPU、内存、I/O 等)。
  • 锁机制:大文件传输可能需要锁定部分资源,影响系统性能。

2.5. 应用层面的问题

  • 超时问题:大文件传输时间较长,可能超过默认的连接超时时间。
  • 传输失败后的重试:一旦传输中断,重新传输整个文件可能浪费大量时间和带宽。
  • 兼容性:跨平台传输时,文件格式、编码或路径可能存在不兼容问题。

3. 大文件传输技术方案

3.1. 分块传输

原理:将文件分成小块(如 1MB、10MB),逐块进行传输。

优点:减少内存占用。网络中断时,仅需重新传输未完成的部分,而非整个文件。

常用工具/技术:HTTP 分片上传:如阿里云 OSS 或 AWS S3 的分片上传。gRPC 流式传输:适合逐块传输大文件。

3.2. 断点续传

原理:在传输中断时记录传输进度,重新连接后从中断点继续。

关键技术:客户端和服务端共同维护文件传输的偏移量(offset)。例如:HTTP 的 Range 头支持分段请求

优点:提升传输可靠性。避免重复传输已完成的部分。

3.3. 流式传输

原理:按需读取和发送文件数据,而不是一次性加载整个文件到内存。

使用场景:gRPC:支持流式消息传输。Socket:通过流式 I/O 逐块发送和接收数据。

优点:降低内存消耗。适合超大文件传输(如 >1GB 文件)。

3.4. 校验完整性

传输完成后,通过校验和(如 MD5SHA-256)验证文件完整性。

优化方式:在传输过程中按块计算校验和,避免传输完成后才校验整个文件。

3.5. 限流与优先级控制

限流:对上传或下载速度进行限制,避免占用过多带宽。

优先级:为重要文件传输设置更高优先级,确保快速完成。

3.6. 使用专用工具或协议

FTP/SFTP:传统的文件传输协议,支持断点续传。优点:成熟稳定,支持大文件传输。

第三方工具:阿里云 OSS、AWS S3、Google Drive 等工具均支持大文件分片上传。

3.7. 大文件传输方案总结

问题

描述

解决方法

传输中断

网络中断或超时导致传输失败

实现断点续传,分块传输。

内存不足

文件过大导致内存溢出(OutOfMemoryError)

使用流式传输或分块处理。

传输速度慢

网络带宽不足或文件过大

压缩文件,或采用多线程并发传输。

校验耗时长

文件过大时计算校验和耗时较长

分块校验,每块单独计算和验证。

多用户资源争抢

多用户同时传输大文件时,服务器资源(CPU、带宽等)可能耗尽

实现限流、负载均衡,或引入 CDN。

传输失败后重复传输浪费

中断后需要重新传输整个文件,浪费时间和带宽

实现断点续传,仅重新传输未完成部分。

文件损坏

网络传输中的数据丢失导致文件损坏

传输完成后通过校验和验证完整性(MD5、SHA-256)。

4. 大文件传输功能实现

4.1. 分块传输实现(java)

思路:将文件分成若干小块,逐块传输。客户端和服务器共同管理块的顺序和大小。

4.1.1. 客户端:分块上传

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class ChunkedFileUploader {
    public static void main(String[] args) throws Exception {
        String filePath = "path/to/large/file.zip";
        String serverUrl = "http://localhost:8080/upload";
        int chunkSize = 1024 * 1024; // 每块 1MB

        File file = new File(filePath);
        FileInputStream fis = new FileInputStream(file);
        long fileLength = file.length();
        long offset = 0;

        int chunkIndex = 0;
        byte[] buffer = new byte[chunkSize];
        while (offset < fileLength) {
            int bytesRead = fis.read(buffer);
            if (bytesRead == -1) break;

            // 上传当前块
            boolean success = uploadChunk(serverUrl, buffer, bytesRead, file.getName(), chunkIndex, fileLength);
            if (!success) {
                System.out.println("Failed to upload chunk " + chunkIndex);
                break;
            }
            offset += bytesRead;
            chunkIndex++;
        }
        fis.close();
    }

    private static boolean uploadChunk(String serverUrl, byte[] chunkData, int bytesRead, String fileName, int chunkIndex, long totalSize) throws Exception {
        HttpURLConnection connection = (HttpURLConnection) new URL(serverUrl).openConnection();
        connection.setDoOutput(true);
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Type", "application/octet-stream");
        connection.setRequestProperty("File-Name", fileName);
        connection.setRequestProperty("Chunk-Index", String.valueOf(chunkIndex));
        connection.setRequestProperty("Total-Size", String.valueOf(totalSize));

        try (OutputStream os = connection.getOutputStream()) {
            os.write(chunkData, 0, bytesRead);
        }

        return connection.getResponseCode() == 200;
    }
}

4.1.2. 服务端:接收分块上传

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

@RestController
@RequestMapping("/upload")
public class FileUploadController {

    @PostMapping
    public String uploadChunk(@RequestHeader("File-Name") String fileName,
                              @RequestHeader("Chunk-Index") int chunkIndex,
                              @RequestHeader("Total-Size") long totalSize,
                              @RequestBody byte[] chunkData) throws Exception {
        String outputDir = "path/to/uploaded/files/";
        File outputFile = new File(outputDir + fileName);

        // 按块写入
        try (OutputStream os = new FileOutputStream(outputFile, true)) { // true: 追加模式
            os.write(chunkData);
        }

        System.out.println("Received chunk " + chunkIndex + ", size: " + chunkData.length);
        return "Chunk " + chunkIndex + " uploaded successfully!";
    }
}

4.2. 断点续传实现(java)

思路:客户端记录每次上传完成的块索引(offset)。如果传输中断,从上次成功的位置重新开始。

4.2.1. 客户端:带断点续传

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

public class ResumableFileUploader {
    public static void main(String[] args) throws Exception {
        String filePath = "path/to/large/file.zip";
        String serverUrl = "http://localhost:8080/upload";
        int chunkSize = 1024 * 1024; // 每块 1MB

        File file = new File(filePath);
        long fileLength = file.length();
        long offset = getUploadedOffset(serverUrl, file.getName()); // 获取已上传的偏移量
        FileInputStream fis = new FileInputStream(file);
        fis.skip(offset);

        byte[] buffer = new byte[chunkSize];
        int chunkIndex = (int) (offset / chunkSize);

        while (offset < fileLength) {
            int bytesRead = fis.read(buffer);
            if (bytesRead == -1) break;

            boolean success = uploadChunk(serverUrl, buffer, bytesRead, file.getName(), chunkIndex, fileLength);
            if (!success) {
                System.out.println("Failed to upload chunk " + chunkIndex);
                break;
            }
            offset += bytesRead;
            chunkIndex++;
        }
        fis.close();
    }

    private static long getUploadedOffset(String serverUrl, String fileName) throws Exception {
        HttpURLConnection connection = (HttpURLConnection) new URL(serverUrl + "?fileName=" + fileName).openConnection();
        connection.setRequestMethod("GET");
        if (connection.getResponseCode() == 200) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            return Long.parseLong(reader.readLine());
        }
        return 0;
    }

    private static boolean uploadChunk(String serverUrl, byte[] chunkData, int bytesRead, String fileName, int chunkIndex, long totalSize) throws Exception {
        HttpURLConnection connection = (HttpURLConnection) new URL(serverUrl).openConnection();
        connection.setDoOutput(true);
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Type", "application/octet-stream");
        connection.setRequestProperty("File-Name", fileName);
        connection.setRequestProperty("Chunk-Index", String.valueOf(chunkIndex));
        connection.setRequestProperty("Total-Size", String.valueOf(totalSize));

        try (OutputStream os = connection.getOutputStream()) {
            os.write(chunkData, 0, bytesRead);
        }

        return connection.getResponseCode() == 200;
    }
}

4.2.2. 服务端:支持断点续传

import org.springframework.web.bind.annotation.*;
import java.io.File;

@RestController
@RequestMapping("/upload")
public class ResumableFileUploadController {

    @GetMapping
    public long getUploadedOffset(@RequestParam("fileName") String fileName) {
        File file = new File("path/to/uploaded/files/" + fileName);
        return file.exists() ? file.length() : 0;
    }

    @PostMapping
    public String uploadChunk(@RequestHeader("File-Name") String fileName,
                              @RequestHeader("Chunk-Index") int chunkIndex,
                              @RequestHeader("Total-Size") long totalSize,
                              @RequestBody byte[] chunkData) throws Exception {
        String outputDir = "path/to/uploaded/files/";
        File outputFile = new File(outputDir + fileName);

        try (OutputStream os = new FileOutputStream(outputFile, true)) { // true: 追加模式
            os.write(chunkData);
        }

        return "Chunk " + chunkIndex + " uploaded successfully!";
    }
}

4.3. 流式传输(gRPC 示例)

4.3.1. Proto 文件示例

syntax = "proto3";

// 定义服务包名
package fileupload;

// 指定 Java 代码生成的包名(可选)
option java_package = "com.example.fileupload";
option java_outer_classname = "FileUploadProto";

// 文件上传服务定义
service FileUploadService {
    // 文件上传接口(客户端流模式)
    rpc UploadFile(stream UploadRequest) returns UploadResponse;
}

// 文件上传请求消息
message UploadRequest {
    string fileName = 1;         // 文件名
    bytes chunkData = 2;         // 当前块的二进制数据
    int64 chunkIndex = 3;        // 当前块的索引(可选)
    int64 totalChunks = 4;       // 总块数(可选)
}

// 文件上传响应消息
message UploadResponse {
    string status = 1;           // 上传状态,例如 "Success"
    string message = 2;          // 附加信息,例如错误原因
}

4.3.2. Proto 文件的使用

生成 gRPC 代码 使用 protoc 生成对应的 Java 文件:

protoc --java_out=. --grpc-java_out=. fileupload.proto

Maven 插件生成:

<plugin>
  <groupId>io.grpc</groupId>
  <artifactId>protoc-gen-grpc-java</artifactId>
  <version>1.57.2</version>
</plugin>

服务端实现:继承生成的 FileUploadServiceGrpc.FileUploadServiceImplBase,实现 UploadFile 方法。

客户端实现:使用 FileUploadServiceGrpc.FileUploadServiceStub 创建流式调用,逐块上传文件数据。

4.3.3. gRPC 服务端实现(java)

import io.grpc.stub.StreamObserver;
import java.io.FileOutputStream;

public class FileUploadServiceImpl extends FileUploadServiceGrpc.FileUploadServiceImplBase {

    @Override
    public StreamObserver<UploadRequest> uploadFile(StreamObserver<UploadResponse> responseObserver) {
        return new StreamObserver<UploadRequest>() {
            private FileOutputStream fos;

            @Override
            public void onNext(UploadRequest request) {
                try {
                    // 初始化文件流
                    if (fos == null) {
                        fos = new FileOutputStream("uploaded/" + request.getFileName());
                    }
                    // 写入当前块的数据
                    fos.write(request.getChunkData().toByteArray());
                } catch (Exception e) {
                    responseObserver.onError(e);
                }
            }

            @Override
            public void onError(Throwable t) {
                try {
                    if (fos != null) fos.close();
                } catch (Exception ignored) {}
                System.err.println("Error during file upload: " + t.getMessage());
            }

            @Override
            public void onCompleted() {
                try {
                    if (fos != null) fos.close();
                } catch (Exception ignored) {}
                // 返回上传成功响应
                responseObserver.onNext(UploadResponse.newBuilder()
                        .setStatus("Success")
                        .setMessage("File uploaded successfully!")
                        .build());
                responseObserver.onCompleted();
            }
        };
    }
}

4.3.4. 客户端实现(java)

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;
import java.io.FileInputStream;

public class FileUploadClient {
    public static void main(String[] args) throws Exception {
        String filePath = "path/to/large/file.zip";
        String fileName = "file.zip";

        // 创建 gRPC 通道
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8080)
                .usePlaintext()
                .build();

        FileUploadServiceGrpc.FileUploadServiceStub stub = FileUploadServiceGrpc.newStub(channel);

        // 创建流式请求
        StreamObserver<UploadRequest> requestObserver = stub.uploadFile(new StreamObserver<UploadResponse>() {
            @Override
            public void onNext(UploadResponse response) {
                System.out.println("Server Response: " + response.getStatus() + " - " + response.getMessage());
            }

            @Override
            public void onError(Throwable t) {
                System.err.println("Error: " + t.getMessage());
            }

            @Override
            public void onCompleted() {
                System.out.println("File upload completed.");
                channel.shutdown();
            }
        });

        // 分块上传文件
        FileInputStream fis = new FileInputStream(filePath);
        byte[] buffer = new byte[1024 * 1024]; // 每块 1MB
        int bytesRead;
        int chunkIndex = 0;
        while ((bytesRead = fis.read(buffer)) != -1) {
            UploadRequest request = UploadRequest.newBuilder()
                    .setFileName(fileName)
                    .setChunkData(com.google.protobuf.ByteString.copyFrom(buffer, 0, bytesRead))
                    .setChunkIndex(chunkIndex++)
                    .build();
            requestObserver.onNext(request);
        }
        fis.close();

        // 完成请求
        requestObserver.onCompleted();
    }
}

博文参考

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux中执行命令

1.1 命令格式 命令格式&#xff1a; 主命令 选项 参数&#xff08;操作对象&#xff09; 命令分为两类&#xff1a; 内置命令&#xff08; builtin &#xff09;&#xff1a;由 shell 程序自带的命令 外部命令&#xff1a;有独立的可执行程序文件&#xff0c;文件名即命令…

Elasticsearch:当混合搜索真正发挥作用时

作者&#xff1a;来自 Elastic Gustavo Llermaly 展示混合搜索何时优于单独的词汇或语义搜索。 在本文中&#xff0c;我们将通过示例探讨混合搜索&#xff0c;并展示它与单独使用词汇或语义搜索技术相比的真正优势。 什么是混合搜索&#xff1f; 混合搜索是一种结合了不同搜索…

Python pyside6 设置的一个《广告图片生成器》

一、图&#xff1a; 二、说明书&#xff1a; 广告图片生成器使用说明 软件功能 这是一个用于生成广告图片的工具&#xff0c;可以快速制作包含产品图片和文字的广告图片。 主要特点 自定义广告尺寸&#xff08;默认620420像素&#xff09; 智能去除产品图片背景 自动排版&…

Spark基本介绍

一&#xff0c;Spark是什么 1.定义&#xff1a;Aache Spark是用于大规模数据处理的统一分析引擎。 二&#xff0c;Spark的发展 三&#xff0c;Spark的特点 高效性 计算速度快 提供了一个全新的数据结构RDD&#xff08;弹性分布式数据集&#xff09;。整个计算操作&#xff0c;…

Elasticsearch操作笔记版

文章目录 1.ES索引库操作(CRUD)1.mapping常见属性(前提)2.创建索引库3.查询&#xff0c;删除索引库4.修改索引库 2.ES文档操作(CRUD)1.新增文档2.查询、删除文档查询返回的数据解读&#xff1a; 3.修改文档 3.RestClient操作(索引库/文档)(CRUD)1.什么是RestClient2.需要考虑前…

EFEVD: Enhanced Feature Extraction for Smart Contract Vulnerability Detection

假设&#xff0c;攻击者在合约 Dao 内存放有 1 Ether 攻击者调用 withdraw 函数&#xff0c;提取 1 Ether&#xff1b; 函数执行到 require 之后&#xff0c; balances 之前时&#xff0c;6789-6789-6789- contract Dao {function withdraw() public {require(balances[msg.…

我的线代观-秩(向量,矩阵)

都说秩是线代中不可避免的一环&#xff0c;当然&#xff0c;它其中最重要的一环。 我在学习线代之后&#xff0c;也有这种感受&#xff0c;它有着一种很绕的感受。 1.矩阵中 在矩阵中&#xff0c;它的秩是怎么定义的呢。它常常与行列式扯上关系&#xff0c;我们拿三阶矩阵为例…

ES IK分词字典热更新

前言 在使用IK分词器的时候&#xff0c;发现官方默认的分词不满足我们的需求&#xff0c;那么有没有方法可以自定义字典呢&#xff1f; 官方提供了三种方式 一、ik本地文件读取方式 k插件本来已为用户提供自定义词典扩展功能&#xff0c;只要修改配给文件即可&#xff1a; …

基于Spring Boot的电影网站系统

一、技术架构 后端框架&#xff1a;Spring Boot&#xff0c;它提供了自动配置、简化依赖管理、内嵌式容器等特性&#xff0c;使得开发者可以快速搭建起一个功能完备的Web应用。 前端技术&#xff1a;可能采用Vue.js、JS、jQuery、Ajax等技术&#xff0c;结合Element UI等组件库…

C#运动控制系统:雷赛控制卡实用完整例子 C#雷赛开发快速入门 C#雷赛运动控制系统实战例子 C#快速开发雷赛控制卡

雷赛控制技术 DMC系列运动控制卡是一款新型的 PCI/PCIe 总线运动控制卡。可以控制多个步进电机或数字式伺服电机&#xff1b;适合于多轴点位运动、插补运动、轨迹规划、手轮控制、编码器位置检测、IO 控制、位置比较、位置锁存等功能的应用。 DMC3000 系列卡的运动控制函数库功…

android studio 写一个小计时器(版本二)

as版本&#xff1a;23.3.1patch2 例程&#xff1a;timer 在前一个版本的基本上改的&#xff0c;增加了继续的功能&#xff0c;实现方法稍微不同。 动画演示&#xff1a; activity_main.xml <?xml version"1.0" encoding"utf-8"?> <androidx…

python-leetcode-轮转数组

189. 轮转数组 - 力扣&#xff08;LeetCode&#xff09; class Solution:def rotate(self, nums: List[int], k: int) -> None:"""Do not return anything, modify nums in-place instead."""n len(nums)k % n # 如果 k 大于 n&#xff0c;…

亚马逊云科技 | Amazon Nova:智能技术新势力

在2024年亚马逊云科技re:invent大会上&#xff0c;Amazon Nova 系列自研生成式 AI 多模态模型重磅登场&#xff0c;新一代的AI产品-Amazon Nova&#xff0c;隶属于 Amazon Bedrock&#xff0c;一共发布6款大模型&#xff0c;精准切入不同领域&#xff0c;解锁多元业务可能&…

记录第一次跑YOLOV8做目标检测

今天是24年的最后一天&#xff0c;终于要向新世界开始破门了&#xff0c;开始深度学习&#xff0c;YOLO来敲门~ 最近做了一些皮肤检测的功能&#xff0c;在传统的处理中经历了反复挣扎&#xff0c;终于要上YOLO了。听过、看过&#xff0c;不如上手体会过~ 1、YOLO是什么&#x…

从授权校验看SpringBoot自动装配

背景 最近需要实现一个对于系统的授权检测功能&#xff0c;即当SpringBoot应用被启动时&#xff0c;需要当前设备是否具有有效的的授权许可信息&#xff0c;若无则直接退出应用。具体的实现方案请继续看下文。 环境 Ruoyi-Vue SpringBoot3 RuoYi-Vue: &#x1f389; 基于Spr…

【Unity】 HTFramework框架(五十七)通过Tag、Layer批量搜索物体

更新日期&#xff1a;2024年12月30日。 Github源码&#xff1a;[点我获取源码] Gitee源码&#xff1a;[点我获取源码] 索引 问题再现通过Tag搜索物体&#xff08;SearchByTag&#xff09;打开SearchByTag窗口搜索标记指定Tag的所有物体批量修改Tag搜索Undefined状态的所有物体 …

Unity2D无限地图的实现(简单好抄)

说明&#xff1a;本教程实现的是在2D游戏中玩家在游戏中上下左右移动的时候自动进行地图拼接的功能&#xff0c;如果你只想实现左右移动的无限地图&#xff0c;那么这篇博客也能起到一定参考作用。 思路 第一步&#xff1a; 创建一个10*10的2D游戏对象当做地图 第二步创建一个…

艾体宝方案丨全面提升API安全:AccuKnox 接口漏洞预防与修复

一、API 安全&#xff1a;现代企业的必修课 在现代技术生态中&#xff0c;应用程序编程接口&#xff08;API&#xff09;扮演着不可或缺的角色。从数据共享到跨平台集成&#xff0c;API 成为连接企业系统与外部服务的桥梁。然而&#xff0c;伴随云计算的普及与微服务架构的流行…

日期时间选择(设置禁用状态)

目录 1.element文档需要 2.禁用所有过去的时间 3.设置指定日期的禁用时间 <template><div class"block"><span class"demonstration">起始日期时刻为 12:00:00</span><el-date-pickerv-model"value1"type"dat…

SAP学习笔记 - 豆知识14 - Msg 番号 M7562 - 取引Type WL 对应的番号範囲中不存在2025年度 OMBT

这种类似的以前也写过&#xff0c;原因就是自动採番的番号没弄。 比如跨年了&#xff0c;那该新年度的番号范围没弄啊&#xff0c;就会出这种错误。 把番号范围给加一下就可以了。 1&#xff0c;现象 比如点 VL02N 出荷传票变更 画面&#xff0c;点 出库确认 就会出如下错误…