Java实现文件分片上传

news2024/11/16 22:00:09

文章目录

      • Java实现文件分片上传
        • 为什么要使用分片上传
        • 什么是分片上传?
        • 前后端代码

Java实现文件分片上传

为什么要使用分片上传

在需要上传文件时,不可避免地会遇到上传文件内容过大,上传时间太长地问题,采用文件分片上传就可以解决这个问题。

什么是分片上传?

简单的说就是本来是需要一次搬一个很大的东西,比如是一大桶水,一次搬起来比较费事费力。我们可以把这一大桶水分装在几个或几十个或者更多的小瓶里,这样搬运起来就比较省力,也比较方便,等到目的地后,我们在将这些小瓶子里的水都倒回大桶里,这样就完成了一大桶水的搬运工作。这个将一大桶水分成许多小瓶子的过程就是分片的过程,最后将水倒回大桶的过程就是合并的过程。分片与合并也是文件分片上传的重要过程。

前后端代码

这个分片的过程是在前端实现的,上传完成后的合并工作是后端完成的。

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JS分片上传-极速上传</title>
</head>
<body>
<input type="file" name="slice" id="slice" >
<br/>
</body>
<script src="http://libs.baidu.com/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
    $("#slice").change(function(event) {
        var file = $("#slice")[0].files[0];
        PostFile(file,0);
    });
    //执行分片上传
    function PostFile(file,i, uuid){
        var name = file.name,                           //文件名
            size = file.size,                           //总大小
            shardSize = 10 * 1024 * 1024,                //以10MB为一个分片,每个分片的大小
            shardCount = Math.ceil(size / shardSize);   //总片数
        if(i >= shardCount){
            return;
        }
        //判断uuid是否存在
        if (uuid === null || uuid === undefined) {
            uuid = guid();
        }
        //console.log(size,i+1,shardSize);  //文件总大小,第一次,分片大小//
        var start = i * shardSize;
        var end = start + shardSize;
        var packet = file.slice(start, end);  //将文件进行切片
        /*  构建form表单进行提交  */
        var form = new FormData();
        form.append("uuid", uuid);// 前端生成uuid作为标识符传个后台每个文件都是一个uuid防止文件串了
        form.append("data", packet); //slice方法用于切出文件的一部分
        form.append("name", name);
        form.append("totalSize", size);
        form.append("total", shardCount); //总片数
        form.append("index", i + 1); //当前是第几片
        $.ajax({
            url: "http://127.0.0.1:8080/index/doPost",
            type: "POST",
            data: form,
            //timeout:"10000",  //超时10秒
            async: true, //异步
            dataType:"json",
            processData: false, //很重要,告诉jquery不要对form进行处理
            contentType: false, //很重要,指定为false才能形成正确的Content-Type
            success: function (msg) {
                console.log(msg);
                /*  表示上一块文件上传成功,继续下一次  */
                if (msg.status === 201) {
                    form = '';
                    i++;
                    PostFile(file, i, uuid);
                } else if (msg.status === 502) {
                    form = '';
                    /*  失败后,每2秒继续传一次分片文件  */
                    setInterval(function () { PostFile(file, i, uuid) }, 2000);
                } else if (msg.status === 200) {
                    merge(uuid, name)
                    console.log("上传成功");
                } else if (msg.status === 500) {
                    console.log('第'+msg.i+'次,上传文件有误!');
                } else {
                    console.log('未知错误');
                }
            }
        })
    }

    function merge(uuid, fileName) {
        $.ajax({
            url: "http://127.0.0.1:8080/index/merge",
            type: "GET",
            data: {uuid: uuid, newFileName: fileName},
            //timeout:"10000",  //超时10秒
            async: true, //异步
            dataType:"json",
            success: function (msg) {
                console.log(msg);
            }
        })
    }

    function guid() {
        return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            var r = Math.random() * 16 | 0,
                v = c === 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }
</script>
</html>

后端代码:

import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/index")
public class test_controller {
    private static final String fileUploadTempDir = "D:/portalupload/fileuploaddir";
    private static final String fileUploadDir = "D:/portalupload/file";
    @RequestMapping("/doPost")
    @ResponseBody
    public Map fragmentation(HttpServletRequest req, HttpServletResponse resp) {
        resp.addHeader("Access-Control-Allow-Origin", "*");
        Map<String, Object> map = new HashMap<>();

        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) req;

        // 获得文件分片数据
        MultipartFile file = multipartRequest.getFile("data");
//        分片第几片
        int index = Integer.parseInt(multipartRequest.getParameter("index"));
//        总片数
        int total = Integer.parseInt(multipartRequest.getParameter("total"));
//        获取文件名
        String fileName = multipartRequest.getParameter("name");
        String name = fileName.substring(0, fileName.lastIndexOf("."));
        String fileEnd = fileName.substring(fileName.lastIndexOf("."));
//        前端uuid,作为标识
        String uuid = multipartRequest.getParameter("uuid");

        File uploadFile = new File(fileUploadTempDir + "/" + uuid, uuid + name + index + ".tem");

        if (!uploadFile.getParentFile().exists()) {
            uploadFile.getParentFile().mkdirs();
        }

        if (index < total) {
            try {
                file.transferTo(uploadFile);
                // 上传的文件分片名称
                map.put("status", 201);
                return map;
            } catch (IOException e) {
                e.printStackTrace();
                map.put("status", 502);
                return map;
            }
        } else {
            try {
                file.transferTo(uploadFile);
                // 上传的文件分片名称
                map.put("status", 200);
                return map;
            } catch (IOException e) {
                e.printStackTrace();
                map.put("status", 502);
                return map;
            }
        }
    }
    @RequestMapping(value = "/merge", method = RequestMethod.GET)
    @ResponseBody
    public Map merge(String uuid, String newFileName) {
        System.out.println(newFileName);
        Map retMap = new HashMap();
        try {
            File dirFile = new File(fileUploadTempDir + "/" + uuid);
            if (!dirFile.exists()) {
                throw new RuntimeException("文件不存在!");
            }
            //分片上传的文件已经位于同一个文件夹下,方便寻找和遍历(当文件数大于十的时候记得排序用冒泡排序确保顺序是正确的)
            String[] fileNames = dirFile.list();
            String name = newFileName.substring(0, newFileName.lastIndexOf("."));
            Arrays.sort(fileNames, (o1,o2)->{
                int i1 = Integer.parseInt(o1.substring(o1.indexOf(name)+name.length()).split("\\.tem")[0]);
                int i2 = Integer.parseInt(o2.substring(o2.indexOf(name)+name.length()).split("\\.tem")[0]);
                return i1 - i2;
            });

//       创建空的合并文件
            File targetFile = new File(fileUploadDir, newFileName);
            if (!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdirs();
            }
            RandomAccessFile writeFile = new RandomAccessFile(targetFile, "rw");

            long position = 0;
            for (String fileName : fileNames) {
                System.out.println(fileName);
                File sourceFile = new File(fileUploadTempDir + "/" + uuid, fileName);
                RandomAccessFile readFile = new RandomAccessFile(sourceFile, "rw");
                int chunksize = 1024 * 3;
                byte[] buf = new byte[chunksize];
                writeFile.seek(position);
                int byteCount;
                while ((byteCount = readFile.read(buf)) != -1) {
                    if (byteCount != chunksize) {
                        byte[] tempBytes = new byte[byteCount];
                        System.arraycopy(buf, 0, tempBytes, 0, byteCount);
                        buf = tempBytes;
                    }
                    writeFile.write(buf);
                    position = position + byteCount;
                }
                readFile.close();
                FileUtils.deleteQuietly(sourceFile);//删除缓存的临时文件
            }
            writeFile.close();
            retMap.put("code", "200");
        }catch (IOException e){
            e.printStackTrace();
            retMap.put("code", "500");
        }
        return retMap;
    }
}

测试:

在这里插入图片描述

可以看到文件进行了分片上传,我是上传了一个系统镜像文件,4GB多,上传的也是非常的快。

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

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

相关文章

ARM-M架构移植UCOS操作系统

最近准备面试&#xff0c;把本科到现在的一些比赛相关的东西整理一下。那些年在飞思卡尔在K60单片机上的UCOSII移植&#xff08;哎&#xff0c;心酸...&#xff09; 一、首先看下UCOSII的文件结构&#xff1a; 一些核心的文件解释&#xff1a; 【1、头文件】&#xff1a; inclu…

SQL多表查询常用语句总结

一、多表关系 &#xff08;一&#xff09;概述 项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求及业务模块之间的关系&#xff0c;分析并设计表结构&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也存在着各种联系&#xff0c;…

【MySQL】数据库中表的操作

表的操作 一、创建表 --- create table 表名(列名 类型 ...);1.1 创建表的案例 二、查看表2.1 查看选中数据库的所有表 --- show tables;2.2 查看&#xff08;描述&#xff09;表结构 --- desc 表名;2.3 查看建表信息 --- show create table 表名 \G 三、修改表 --- alter3.1 修…

说说我认为的AI和人类的未来

今天闲来无事&#xff0c;花了半天时间部署了一个Stable Diffusion的开源AI生成图片程序&#xff0c;因为电脑配置的原因&#xff0c;找了一个小模型集测试了一下效果&#xff0c;总的来说还是挺震惊的。上图是通过输入&#xff1a;长发美女森林漫步红色裙子 这三个关键词后花了…

useEffect的基础知识和底层机制

useEffect 是 React 中一个重要的 Hook&#xff0c;用来处理组件的副作用操作。它的基础知识包括两个方面&#xff1a;执行时机和参数。 执行时机&#xff1a; useEff ect 的执行时机包括两种情况&#xff1a; 组件挂载时&#xff0c;即第一次渲染之后。组件更新时&#xff…

pandas---Series与DataFrame索引、切片;多层索引、索引的堆叠

1. Series的索引和切片 1.1 Series的索引&#xff1a; 可以使用中括号取单个索引&#xff08;此时返回的是元素类型&#xff09;&#xff0c;或者中括号里一个列表取多个索引&#xff08;此时 返回的仍然是一个Series类型&#xff09;。分为显示索引和隐式索引&#xff1a; …

后台服务接口间大文件的流式发送和读取

文章目录 介绍代码设计代码参考客户端代码服务器端代码测试实例 介绍 使用HTTP协议进行数据流式传输是一种常见的方法。对于大文件数据传输可以使用HTTP的chunked编码或使用多部分响应来实现数据流式传输。 【HTTP的chunked编码】在发送数据的服务中&#xff0c;可以将数据切分…

postman接口测试学习笔记(非常详细)

目录 引言 1. 资源 2.接口测试 3. 接口返回数据和JSON详解 4.接口测试协议 5.企业接口测试的流程和方案 6. 接口测试工具以及Postman介绍 7.Postman之内置的动态参数和应用 8.接口关联 引言 接口测试工具如何选择&#xff1f;在我看来&#xff0c;Postman是一个非常值…

电商--红包活动总结

文章目录 背景表结构设计实现流程流程图流程解析流程优化 背景 这是一个来自于NFT电商项目&#xff0c;这是个营销策略的需求&#xff0c;为了快狠准&#xff0c;短期内刺激消费&#xff0c;拉动销售增加购买量。运营人员可以在平台创建红包&#xff0c;并且设置该红包的发放时…

Android系统视角下对APK的分析(1)- 静态分析APK文件

声明 以Android手机用户角度来看&#xff0c;安装各式各样的APP&#xff0c;基本就是从应用市场上 “搜索->下载->安装” 三连。而对Android系统来说&#xff0c;这就是个大工程了&#xff0c;因为对Android系统来说APK是“外来户”&#xff0c;如何安装它、有限制地支持…

【用户认证】密码加密,用户状态保存,cookie,session,token

相关概念 认证与授权 认证&#xff08;authentication &#xff09;是验证你的身份的过程&#xff0c;而授权&#xff08;authorization&#xff09;是验证你有权访问的过程 用户认证的逻辑 获取用户提交的用户名和密码根据用户名&#xff0c;查询数据库&#xff0c;获得完…

redis知识复习

redis知识复习 redis基础知识redis的认识一. 非关系型数据库 与 传统数据库 的区别二. 安装redis并设置自启动三. 熟悉命令行客户端四.熟悉图形化工具RDM redis的命令与数据结构一. 数据结构介绍二. redis通用命令&#xff08;熟练掌握&#xff09; redis的Java客户端一. Jedis…

文心一言 VS 讯飞星火 VS chatgpt (38)-- 算法导论5.4 2题

二、假设我们将球投入到b个箱子里&#xff0c;直到某个箱子中有两个球。每一次投掷都是独立的并且每个球落入任何箱子的机会均等。请问投球次数期望是多少? 文心一言&#xff1a; 这是一个典型的鸽巢原理&#xff08;Pigeonhole Principle&#xff09;问题。假设每次投球时&…

2023年上半年系统分析师下午真题及答案解析

试题一(25分) (试题一为必答题&#xff0c;试题二~五选答两题) 某软件公司拟开发一套汽车租赁系统&#xff0c;科学、安全和方便的管理租赁公司的各项业务&#xff0c;提高公司效率&#xff0c;提升利用率。注册用户在使用系统进行车辆预约时需执行以下操作&#xff1a;(a)用…

从0-1一起学习live555设计思想之二 RTSP交互过程

流媒体服务系列 文章目录 流媒体服务系列前言一、OPTION二、DESCRIBE三、SETUP四、PLAY总结前言 本篇文章通过代码去分析rtsp交互过程与工作原理。由于live555的继承关系太过复杂,所以做了个图简单记录一下与h264文件传输相关的类继承关系。 一、OPTION OPTION比较简单,就…

【调制BFSK】二进制频移键控FSK的数字调制(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

ARM学习(22)断点认识以及调试

笔者来聊聊断点以及断点的调试 1、断点原理 断电的原理一般分为两种&#xff0c;插入断点指令或者利用硬件调试寄存器进行断点。 前者程序如果在RAM&#xff08;SRAM、DDR&#xff09;上&#xff0c;则调试器可以直接在断点地址处插入断点指令&#xff0c;例如BKPT&#xff0…

python自动化测试-自动化基本技术原理

1 概述 在之前的文章里面提到过&#xff1a;做自动化的首要本领就是要会 透过现象看本质 &#xff0c;落实到实际的IT工作中就是 透过界面看数据。 掌握上面的这样的本领可不是容易的事情&#xff0c;必须要有扎实的计算机理论基础&#xff0c;才能看到深层次的本质东西。 …

家用电器-空调制冷、制热、除霜、除湿、换新风的基本原理及实现讲解

目录 一、空调历史 二、空调的作用 三、空调类型 四、基本原理 4.1 制冷过程 4.2 制热过程 4.3 除霜过程 4.4 除湿过程 4.5 换气过程 五、电路控制系统 六、核心部件 七、基本指标 1&#xff09;气候类型 2&#xff09;额定制冷量 3&#xff09;能效比 八、市场…