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

news2025/1/5 21:54:59

摘要

大文件传输是指通过网络将体积较大的文件从一个位置发送到另一个位置的过程。这些文件可能包括高清视频、大型数据库、复杂的软件安装包等,它们的大小通常超过几百兆字节(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();
    }
}

博文参考

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

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

相关文章

【老白学 Java】简单位移动画

简单位移动画 文章来源&#xff1a;《Head First Java》修炼感悟。 上一篇文章中&#xff0c;老白利用内部类的特性完成了多个事件的处理&#xff0c;感觉还不错。 为了更深入理解内部类&#xff0c;本篇文章继续使用内部类创建一个画板&#xff0c;完成简单的位移动画&#x…

彻底解决 Selenium ChromeDriver 不匹配问题:Selenium ChromeDriver 最新版本下载安装教程

在 Python 的 Selenium 自动化测试中&#xff0c;ChromeDriver 是不可或缺的工具。它作为代码与 Chrome 浏览器的桥梁&#xff0c;但如果版本不匹配&#xff0c;就会导致各种报错&#xff0c;尤其是以下常见问题&#xff1a; selenium.common.exceptions.SessionNotCreatedExc…

[CTF/网络安全] 攻防世界 warmup 解题详析

查看页面源代码&#xff0c;发现source.php 得到一串代码&#xff0c;进行代码审计&#xff1a; <?phpclass emmm{public static function checkFile(&$page){$whitelist ["source">"source.php","hint">"hint.php"];…

基于fMRI数据计算脑脊液(CSF)与全脑BOLD信号的时间耦合分析

一、前言 笔者之前的文章《基于Dpabi和spm12的脑脊液(csf)分割和提取笔记》,介绍了如何从普通的fMRI数据中提取CSF信号。首先是基础的预处理,包括时间层校正、头动校正,再加上0.01-0.1Hz的带通滤波。接着用SPM12分割出CSF区域,设置一个比较严格的0.9阈值,确保提取的真是…

游泳溺水识别数据集,对25729张图片进行YOLO,COCO JSON, VOC XML 格式的标注,溺水平均识别率在89.9%

游泳溺水识别数据集&#xff0c;对25729张图片进行YOLO&#xff0c;COCO JSON, VOC XML 格式的标注&#xff0c;溺水识别率在92&#xff05; 训练结果 数据集和标签 验证 游泳测试视频 根据测试的视频来获取检测结果&#xff1a; 游泳测试视频的置信度设置60% 检测结果如下&…

STM32 拓展 电源控制

目录 电源控制 电源框图 VDDA供电区域 VDD供电区域 1.8V低电压区域 后备供电区域 电压调节器 上电复位和掉电复位 可编程电压检测器(PVD) 低功耗 睡眠模式(只有CUP(老板)睡眠) 进入睡眠模式 退出睡眠模式 停机(停止)模式(只留核心区域(上班)) 进入停…

Mac M2 Pro安装MySQL 8.4.3

絮絮叨叨 MacBook Pro&#xff0c;芯片&#xff1a;Apple M2 Pro, macOS: Sonoma 14.0一直知道很多软件对Mac M1或M2的支持不好&#xff0c;但没想到在安装MySQL 8.x上也让我吃尽了苦头本文除了介绍如何安装MySQL 8.4.3外&#xff0c;还会记录笔者遇到的一些问题以及解决方法 …

闻泰科技涨停-操盘训练营实战-选股和操作技术解密

如上图&#xff0c;闻泰科技&#xff0c;今日涨停&#xff0c;这是前两天分享布局的一个潜伏短线的标的。 选股思路&#xff1a; 1.主图指标三条智能辅助线粘合聚拢&#xff0c;即将选择方向 2.上图红色框住部分&#xff0c;在三线聚拢位置&#xff0c;震荡筑底&#xff0c;…

ts总结一下

ts基础应用 /*** 泛型工具类型*/ interface IProps {id: string;title: string;children: number[]; } type omita Omit<IProps, id | title>; const omitaA: omita {children: [1] }; type picka Pick<IProps, id | title>; const pickaA: picka {id: ,title…

Linux:各发行版及其包管理工具

相关阅读 Linuxhttps://blog.csdn.net/weixin_45791458/category_12234591.html?spm1001.2014.3001.5482 Debian 包管理工具&#xff1a;dpkg&#xff08;低级包管理器&#xff09;、apt&#xff08;高级包管理器&#xff0c;建立在dpkg基础上&#xff09;包格式&#xff1a;…

2024秋语法分析作业-B(满分25分)

特别注意&#xff1a;第17条产生式改为 17) Stmt → while ( Cond ) Stmt 【问题描述】 本次作业只测试一个含简单变量声明、赋值语句、输出语句、if语句和while语句的文法&#xff1a; 0) CompUnit → Block 1) Block → { BlockItemList } 2) BlockItemList → BlockItem…

Tomcat优化指南

以下是一份详细的Tomcat优化指南&#xff1a; 一、JVM&#xff08;Java虚拟机&#xff09;优化 内存设置 堆内存&#xff08;Heap Memory&#xff09; 调整-Xms&#xff08;初始堆大小&#xff09;和-Xmx&#xff08;最大堆大小&#xff09;参数。一般来说&#xff0c;将初始…

【我的 PWN 学习手札】IO_FILE 之 劫持vtable

vtable帮助C实现了类似于多态的效果&#xff0c;然而其中的大量函数指针&#xff0c;一旦被劫持修改&#xff0c;就会产生巨大的危害。 前言 【我的 PWN 学习手札】IO_FILE相关几个基本函数的调用链源码-CSDN博客 【我的 PWN 学习手札】IO_FILE 之 stdin任意地址写-CSDN博客…

力扣编程从0-1

第一题 class Solution:def mergeAlternately(self, word1: str, word2: str) -> str:#计算两个字符串长度&#xff0c;从i 0开始遍历&#xff0c;每次循环&#xff1a;#如果i小于word1的长度&#xff0c;把word1[i]加到答案末尾#如果i小于word2的长度&#xff0c;把word2[…

SpringMVC(一)配置

目录 引入 第一章&#xff1a;Java web的发展历史 一、Model I和Model II 1.Model I开发模式 2.Model II开发模式 二. MVC模式 第二章&#xff1a;SpringMVC的入门案例 搭建SpringMVC的入门程序 1.创建新项目 2.等待加载导入坐标 3.处理xml文件和其他 导入tomcat 运…

迅为RK3568开发板编译Android12源码包-设置屏幕配置

在源码编译之前首先要确定自己想要使用的屏幕并修改源码&#xff0c;在编译镜像&#xff0c;烧写镜像。如下图所示&#xff1a; 第一步&#xff1a;确定要使用的屏幕种类&#xff0c;屏幕种类选择如下所示&#xff1a; iTOP-3568 开发板支持以下种类屏幕&#xff1a; 迅为 LV…

机器学习-感知机-神经网络-激活函数-正反向传播-梯度消失-dropout

文章目录 感知机工作流程 神经网络区别各种各样的神经网络 激活函数激活函数类型Sigmoid 函数ReLU函数Leaky ReLU 函数Tanh 函数 正向传播反向传播梯度消失(gradient vanish)如何解决 Dropout使用 PyTorch实战神经网络算法(手写MNIST数字识别)viewsoftmax和log-softmaxcross-en…

Android使用JAVA调用JNI原生C++方法

1.native-lib.cpp为要生成so库的源码文件 2.JNI函数声明说明 NewStringUTF函数会返回jstring JNI函数声明规则 3.JAVA中声明及调用JNI函数 声明&#xff1a; 调用 4.源码地址&#xff1a; gitgithub.com:tonyimax/UpdateTimeByThread.git

【git】git stash相关指令

目录 git stashgit stash save “”git stash list&#xff1a; 获取stash列表git stash pop&#xff1a;恢复最近一次stash缓存git stash apply stash{index}: 恢复指定缓存在这里插入图片描述git stash drop stash{1}&#xff1a;删除指定缓存 git stash clear :删除stash gi…

spring的@Transactional事务原理理解

目录 Transactional 普通例子代码和测试输出编程式事务事务代理实现和TransactionAspectSupport重要类复习Spring的事务传播机制有哪些实际工作中用到的事务处理 Transactional事务原理理解 Transactional 普通例子代码和测试输出 Transactional(rollbackFor Exception.class…