实验- 分片上传 VS 直接上传

news2025/3/18 1:21:49

分片上传和直接上传是两种常见的文件上传方式。分片上传将文件分成多个小块,每次上传一个小块,可以并行处理多个分片,适用于大文件上传,减少了单个请求的大小,能有效避免因网络波动或上传中断导致的失败,并支持断点续传。相比之下,直接上传是将文件作为一个整体上传,通常适用于较小的文件,简单快捷,但对于大文件来说,容易受到网络环境的影响,上传中断时需要重新上传整个文件。因此,分片上传在大文件上传中具有更高的稳定性和可靠性。

FileUploadController

package per.mjn.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import per.mjn.service.FileUploadService;

import java.io.File;
import java.io.IOException;
import java.util.List;

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

    private static final String UPLOAD_DIR = "F:/uploads/";

    @Autowired
    private FileUploadService fileUploadService;

    // 启动文件分片上传
    @PostMapping("/start")
    public ResponseEntity<String> startUpload(@RequestParam("file") MultipartFile file,
                                              @RequestParam("totalParts") int totalParts,
                                              @RequestParam("partIndex") int partIndex) {
        try {
            String fileName = file.getOriginalFilename();
            fileUploadService.saveChunk(file, partIndex);  // 存储单个分片
            return ResponseEntity.ok("Chunk " + partIndex + " uploaded successfully.");
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Error uploading chunk: " + e.getMessage());
        }
    }

    // 多线程并行上传所有分片
    @PostMapping("/uploadAll")
    public ResponseEntity<String> uploadAllChunks(@RequestParam("file") List<MultipartFile> files,
                                                  @RequestParam("fileName") String fileName) {
        try {
            fileUploadService.uploadChunksInParallel(files, fileName);  // 调用并行上传方法
            return ResponseEntity.ok("All chunks uploaded successfully.");
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Error uploading chunks: " + e.getMessage());
        }
    }

    // 合并文件分片
    @PostMapping("/merge")
    public ResponseEntity<String> mergeChunks(@RequestParam("fileName") String fileName,
                                              @RequestParam("totalParts") int totalParts) {
        try {
            System.out.println(fileName);
            System.out.println(totalParts);
            fileUploadService.mergeChunks(fileName, totalParts);
            return ResponseEntity.ok("File uploaded and merged successfully.");
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Error merging chunks: " + e.getMessage());
        }
    }

    // 直接上传整个文件
    @PostMapping("/file")
    public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
        try {
            String fileName = file.getOriginalFilename();
            File targetFile = new File(UPLOAD_DIR + fileName);
            File dir = new File(UPLOAD_DIR);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            file.transferTo(targetFile);
            return ResponseEntity.ok("File uploaded successfully: " + fileName);
        } catch (IOException e) {
            return ResponseEntity.status(500).body("Error uploading file: " + e.getMessage());
        }
    }
}

FileUploadService

package per.mjn.service;

import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

public interface FileUploadService {
    public void saveChunk(MultipartFile chunk, int partIndex) throws IOException;
    public void uploadChunksInParallel(List<MultipartFile> chunks, String fileName);
    public void mergeChunks(String fileName, int totalParts) throws IOException;
}

FileUploadServiceImpl

package per.mjn.service.impl;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import per.mjn.service.FileUploadService;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Service
public class FileUploadServiceImpl implements FileUploadService {
    private static final String UPLOAD_DIR = "F:/uploads/";
    private final ExecutorService executorService;

    public FileUploadServiceImpl() {
        // 使用一个线程池来并发处理上传
        this.executorService = Executors.newFixedThreadPool(4); // 4个线程用于并行上传
    }

    // 保存文件分片
    public void saveChunk(MultipartFile chunk, int partIndex) throws IOException {
        File dir = new File(UPLOAD_DIR);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File chunkFile = new File(UPLOAD_DIR + chunk.getOriginalFilename() + ".part" + partIndex);
        chunk.transferTo(chunkFile);
    }

    // 处理所有分片的上传
    public void uploadChunksInParallel(List<MultipartFile> chunks, String fileName) {
        List<Callable<Void>> tasks = new ArrayList<>();
        for (int i = 0; i < chunks.size(); i++) {
            final int index = i;
            final MultipartFile chunk = chunks.get(i);
            tasks.add(() -> {
                try {
                    saveChunk(chunk, index);
                    System.out.println("Uploaded chunk " + index + " of " + fileName);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            });
        }

        try {
            // 执行所有上传任务
            executorService.invokeAll(tasks);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 合并文件分片
    public void mergeChunks(String fileName, int totalParts) throws IOException {
        File mergedFile = new File(UPLOAD_DIR + fileName);
        try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(mergedFile))) {
            for (int i = 0; i < totalParts; i++) {
                File chunkFile = new File(UPLOAD_DIR + fileName + ".part" + i);
                try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(chunkFile))) {
                    byte[] buffer = new byte[8192];
                    int bytesRead;
                    while ((bytesRead = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, bytesRead);
                    }
                }
                chunkFile.delete(); // 删除临时分片文件
            }
        }
    }
}

前端测试界面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload</title>
</head>
<body>
    <h1>File Upload</h1>
    <input type="file" id="fileInput">
    <button onclick="startUpload()">Upload File</button>
    <button onclick="directUpload()">Upload File Directly</button>

    <script>
        const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB per chunk
        const fileInput = document.querySelector('#fileInput');
        let totalChunks;

        function startUpload() {
            const file = fileInput.files[0];
            totalChunks = Math.ceil(file.size / CHUNK_SIZE);

            let currentChunk = 0;
            let files = [];
            while (currentChunk < totalChunks) {
                const chunk = file.slice(currentChunk * CHUNK_SIZE, (currentChunk + 1) * CHUNK_SIZE);
                files.push(chunk);
                currentChunk++;
            }

            // 并行上传所有分片
            uploadAllChunks(files, file.name);
        }

        function uploadAllChunks(chunks, fileName) {
            const promises = chunks.map((chunk, index) => {
                const formData = new FormData();
                formData.append('file', chunk, fileName);
                formData.append('partIndex', index);
                formData.append('totalParts', totalChunks);
                formData.append('fileName', fileName);

                return fetch('http://172.20.10.2:8080/upload/start', {
                    method: 'POST',
                    body: formData
                }).then(response => response.text())
                  .then(data => console.log(`Chunk ${index} uploaded successfully.`))
                  .catch(error => console.error(`Error uploading chunk ${index}`, error));
            });

            // 等待所有分片上传完成
            Promise.all(promises)
                .then(() => {
                    console.log('All chunks uploaded, now merging.');
                    mergeChunks(fileName);
                })
                .catch(error => console.error('Error during uploading chunks', error));
        }

        function mergeChunks(fileName) {
            fetch(`http://172.20.10.2:8080/upload/merge?fileName=${fileName}&totalParts=${totalChunks}`, {
                method: 'POST'
            }).then(response => response.text())
              .then(data => console.log('File uploaded and merged successfully.'))
              .catch(error => console.error('Error merging chunks', error));
        }

        // 直接上传整个文件
        function directUpload() {
            const file = fileInput.files[0];
            if (!file) {
                alert('Please select a file to upload.');
                return;
            }

            const formData = new FormData();
            formData.append('file', file); // 将整个文件添加到 FormData

            fetch('http://172.20.10.2:8080/upload/file', {
                method: 'POST',
                body: formData
            })
            .then(response => response.text())
            .then(data => console.log('File uploaded directly: ', data))
            .catch(error => console.error('Error uploading file directly', error));
        }
    </script>
</body>
</html>

测试分片上传与直接上传耗时

我们上传一个310MB的文件,分片上传每个分片在前端设置为10MB,在后端开5个线程并发执行上传操作。
直接上传没有分片大小也没有开多线程,下面是两种方式的测试结果。
在这里插入图片描述
分片上传,耗时2.419s
在这里插入图片描述

直接上传,耗时4.572s

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

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

相关文章

生活中的可靠性小案例12:类肤材质老化发粘问题

我一直觉得我买的某品牌车载吸尘器很好用&#xff0c;用了几年&#xff0c;目前性能也是杠杠的。然而它现在有个最大的问题&#xff0c;就是表面发粘了&#xff0c;用起来粘手&#xff0c;非常不舒服。 这一类问题在生活中不少见&#xff0c;尤其是一些用了类肤材质涂层的物件。…

qt 自带虚拟键盘的编译使用记录

一、windows 下编译 使用vs 命令窗口&#xff0c;分别执行&#xff1a; qmake CONFIG"lang-en_GB lang-zh_CN" nmake nmake install 如果事先没有 指定需要使用的输入法语言就进行过编译&#xff0c;则需要先 执行 nmake distclean 清理后执行 qmake 才能生效。 …

python中print函数的flush如何使用

在 Python 中&#xff0c;print 函数的 flush 参数是一个布尔值&#xff0c;默认值为 False。当设置为 True 时&#xff0c;它会强制将输出缓冲区的内容立即刷新到目标设备&#xff08;通常是控制台&#xff09;&#xff0c;而不是等待缓冲区满或者程序结束时才输出。 要注意fl…

【软考-架构】5.2、传输介质-通信方式-IP地址-子网划分

✨资料&文章更新✨ GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 文章目录 传输介质网线光纤无线信道 通信方式和交换方式会考&#xff1a;交换方式 &#x1f4af;考试真题第一题第二题 IP地址表示子网划分&#x1f4af;考试真题第一题第二题 传输…

websocket学习手册及python实现简单的聊天室

概述 WebSocket 是一种网络通信协议&#xff0c;允许在单个 TCP 连接上进行全双工通信。它最核心的优势就在于实现了持久连接&#xff0c;实现了实时的数据传输。HTTP 协议有一个很大的缺点&#xff0c;通信只能由客户端发起&#xff0c;服务器返回响应后连接就会关闭&#xf…

SpringMVC (二)请求处理

目录 章节简介 一 请求处理&#xff08;初级&#xff09; eg:请求头 二 请求处理&#xff08;进阶&#xff09; eg:请求体 三 获取请求头 四 获取Cookie 五 级联封装 六 使用RequestBoby封装JSON对象 七 文件的上传 八 获取整个请求 HttpEntity 九 原生请求 Spring…

1.6、Java继承、构造方法、数组

子类可以增加字段、增加方法或覆盖父类方法&#xff0c;但继承不会删除任何字段和方法不恰当认为super 同 this 引用是类似的概念&#xff0c;其实super不是一个对象的引用&#xff0c;不能将值super赋给另一个对象变量&#xff0c;super只是一个指示 编译器调用父类方法的特殊…

通义万相 2.1 与蓝耘智算平台的深度协同,挖掘 AIGC 无限潜力并释放巨大未来价值

我的个人主页 我的专栏&#xff1a; 人工智能领域、java-数据结构、Javase、C语言&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01; 点赞&#x1f44d;收藏❤ 引言&#xff1a;AIGC 浪潮下的新机遇 在当今数字化飞速发展的时代&#xff0c;人工智能生成内容&…

专题|Python贝叶斯金融数据应用实例合集:随机波动率SV模型、逻辑回归、参数更新、绩效比较BEST分析亚马逊股票、普尔指数...

原文链接&#xff1a;https://tecdat.cn/?p41020 本专题合集系统梳理了贝叶斯方法在金融数据分析与分类建模中的前沿应用。合集聚焦于PyMC3概率编程框架&#xff0c;深度探讨了共轭先验参数更新、贝叶斯逻辑回归、贝叶斯夏普比率等核心算法在实际场景中的落地实践&#xff08;…

Linux调度器 --- 负载均衡的存在的问题

文章目录 前言一、简介二、Linux 调度器2.1 在单核系统上&#xff0c;CFS 非常简单2.2 在多核系统上&#xff0c;CFS 变得非常复杂2.2.1 负载均衡算法2.2.2 优化措施 三、Linux调度器负载均衡的存在的问题3.1 组负载不均衡问题&#xff08;Group Imbalance Bug&#xff09;3.2 …

从零开始用AI开发游戏(三)背景故事

《迷域回响》背景故事 第一章&#xff1a;失落的符文纪元 在远古的“艾瑟兰”大陆&#xff0c;掌握空间魔法的「筑界者文明」曾建造了连通万界的回响迷宫——这座迷宫既是试炼场&#xff0c;也是囚笼。文明巅峰时期&#xff0c;筑界者将禁忌知识刻入虚空符文&#xff0c;嵌于…

IXTUR气控永磁铁:以高精度气控和稳定磁场,为机器人应用提供稳定抓取力

在现代工业生产和物流领域&#xff0c;物料的抓取与搬运是影响生产效率和成本控制的重要环节。传统夹爪在面对不同材质、形状和重量的物体时&#xff0c;常常存在适应性差、抓取不稳定、操作复杂等问题&#xff0c;导致生产流程中频繁出现停机调整&#xff0c;增加了人工干预成…

硬件驱动——51单片机:寄存器、LED、动态数码管

目录 一、51单片机 1.寄存器 二、LED点灯 1.原理 2.封装函数 3.顺序点灯 4.特定位点灯 三、动态数码管 1.原理 2.封装函数 3.0~9跳变 4.顺序移位0~9跳变 一、51单片机 1.寄存器 51单片机共40个引脚&#xff0c;其中P0,P1,P2,P3是四个有8引脚的寄存器&#xff0…

2025 香港 Web3 嘉年华:全球 Web3 生态的年度盛会

自 2023 年首届香港 Web3 嘉年华成功举办以来&#xff0c;这一盛会已成为全球 Web3 领域规模最大、影响力最深远的行业活动之一。2025 年 4 月 6 日至 9 日&#xff0c;第三届香港 Web3 嘉年华将在香港盛大举行。本届活动由万向区块链实验室与 HashKey Group 联合主办、W3ME 承…

【MySQL】多表查询(笛卡尔积现象,联合查询、内连接、左外连接、右外连接、子查询)-通过练习快速掌握法

在DQL的基础查询中&#xff0c;我们已经学过了多表查询的一种&#xff1a;联合查询&#xff08;union&#xff09;。本文我们将系统的讲解多表查询。 笛卡尔积现象 首先&#xff0c;我们想要查询emp表和stu表两个表&#xff0c;按照我们之前的知识栈&#xff0c;我们直接使用…

Leetcode-132.Palindrome Partitioning II [C++][Java]

目录 一、题目描述 二、解题思路 【C】 【Java】 Leetcode-132.Palindrome Partitioning IIhttps://leetcode.com/problems/palindrome-partitioning-ii/description/132. 分割回文串 II - 力扣&#xff08;LeetCode&#xff09;132. 分割回文串 II - 给你一个字符串 s&…

在 macOS 上优化 Vim 用于开发

简介 这篇指南将带你通过一系列步骤&#xff0c;如何在 macOS 上优化 Vim&#xff0c;使其具备 代码补全、语法高亮、代码格式化、代码片段管理、目录树等功能。此外&#xff0c;我们还会解决在安装过程中可能遇到的常见错误。 1. 安装必备工具 在开始 Vim 配置之前&#xff…

SOME/IP-SD -- 协议英文原文讲解8

前言 SOME/IP协议越来越多的用于汽车电子行业中&#xff0c;关于协议详细完全的中文资料却没有&#xff0c;所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块&#xff1a; 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 5.1.4.4 S…

【Agent实战】货物上架位置推荐助手(RAG方式+结构化prompt(CoT)+API工具结合ChatGPT4o能力Agent项目实践)

本文原创作者:姚瑞南 AI-agent 大模型运营专家,先后任职于美团、猎聘等中大厂AI训练专家和智能运营专家岗;多年人工智能行业智能产品运营及大模型落地经验,拥有AI外呼方向国家专利与PMP项目管理证书。(转载需经授权) 目录 结论 效果图示 1.prompt 2. API工具封…

PyTorch 深度学习实战(11):强化学习与深度 Q 网络(DQN)

在之前的文章中&#xff0c;我们介绍了神经网络、卷积神经网络&#xff08;CNN&#xff09;、循环神经网络&#xff08;RNN&#xff09;、Transformer 等多种深度学习模型&#xff0c;并应用于图像分类、文本分类、时间序列预测等任务。本文将介绍强化学习的基本概念&#xff0…