vue+springboot上传大文件的实现示例

目录
  • 前言
  • 逻辑
  • 前端
  • 后端
    • RedisTemplate配置
  • 结语

前言

众所周知,上传大文件是一件很麻烦的事情,假如一条路走到黑,直接一次性把文件上传上去,对于小文件是可以这样做,但是对于大文件可能会出现网络问题,请求响应时长等等导致文件上传失败,那么这次来教大家如何用vue+srpingboot项目上传大文件

逻辑

需要做大文件上传应该考虑到如下逻辑:

  • 大文件上传一般需要将文件切片(chunk)上传,然后再将所有切片合并为完整的文件。可以按以下逻辑进行实现:
  • 前端在页面中选择要上传的文件,并使用Blob.slice方法对文件进行切片,一般每个切片大小为固定值(比如5MB),并记录总共有多少个切片。
  • 将切片分别上传到后端服务,可以使用XMLHttpRequest或Axios等库发送Ajax请求。对于每个切片,需要包含三个参数:当前切片索引(从0开始)、切片总数、切片文件数据。
  • 后端服务接收到切片后,保存到指定路径下的临时文件中,并记录已上传的切片索引和上传状态。如果某个切片上传失败,则通知前端重传该切片。
  • 当所有切片都上传成功后,后端服务读取所有切片内容并将其合并为完整的文件。可以使用java.io.SequenceInputStream和BufferedOutputStream来实现文件合并。
  • 最后返回文件上传成功的响应结果给前端即可。

前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>File Upload</title>
</head>
<body>
    <input type="file" id="fileInput">
    <button onclick="upload()">Upload</button>
    <script>
        function upload() {
            let file = document.getElementById("fileInput").files[0];
            let chunkSize = 5 * 1024 * 1024; // 切片大小为5MB
            let totalChunks = Math.ceil(file.size / chunkSize); // 计算切片总数
            let index = 0;
            while (index < totalChunks) {
                let chunk = file.slice(index * chunkSize, (index + 1) * chunkSize);
                let formData = new FormData();
                formData.append("file", chunk);
                formData.append("index", index);
                formData.append("totalChunks", totalChunks);
                // 发送Ajax请求上传切片
                $.ajax({
                    url: "/uploadChunk",
                    type: "POST",
                    data: formData,
                    processData: false,
                    contentType: false,
                    success: function () {
                        if (++index >= totalChunks) {
                            // 所有切片上传完成,通知服务端合并文件
                            $.post("/mergeFile", {fileName: file.name}, function () {
                                alert("Upload complete!");
                            })
                        }
                    }
                });
            }
        }
    </script>
</body>
</html>

后端

controller层:

@RestController
public class FileController {

    @Value("${file.upload-path}")
    private String uploadPath;

    @PostMapping("/uploadChunk")
    public void uploadChunk(@RequestParam("file") MultipartFile file,
                            @RequestParam("index") int index,
                            @RequestParam("totalChunks") int totalChunks) throws IOException {
        // 以文件名+切片索引号为文件名保存切片文件
        String fileName = file.getOriginalFilename() + "." + index;
        Path tempFile = Paths.get(uploadPath, fileName);
        Files.write(tempFile, file.getBytes());
        // 记录上传状态
        String uploadFlag = UUID.randomUUID().toString();
        redisTemplate.opsForList().set("upload:" + fileName, index, uploadFlag);
        // 如果所有切片已上传,则通知合并文件
        if (isAllChunksUploaded(fileName, totalChunks)) {
            sendMergeRequest(fileName, totalChunks);
        }
    }

    @PostMapping("/mergeFile")
    public void mergeFile(String fileName) throws IOException {
        // 所有切片均已成功上传,进行文件合并
        List<File> chunkFiles = new ArrayList<>();
        for (int i = 0; i < getTotalChunks(fileName); i++) {
            String chunkFileName = fileName + "." + i;
            Path tempFile = Paths.get(uploadPath, chunkFileName);
            chunkFiles.add(tempFile.toFile());
        }
        Path destFile = Paths.get(uploadPath, fileName);
        try (OutputStream out = Files.newOutputStream(destFile);
             SequenceInputStream seqIn = new SequenceInputStream(Collections.enumeration(chunkFiles));
             BufferedInputStream bufIn = new BufferedInputStream(seqIn)) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bufIn.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
        }
        // 清理临时文件和上传状态记录
        for (int i = 0; i < getTotalChunks(fileName); i++) {
            String chunkFileName = fileName + "." + i;
            Path tempFile = Paths.get(uploadPath, chunkFileName);
            Files.deleteIfExists(tempFile);
            redisTemplate.delete("upload:" + chunkFileName);
        }
    }

    private int getTotalChunks(String fileName) {
        // 根据文件名获取总切片数
        return Objects.requireNonNull(Paths.get(uploadPath, fileName).toFile().listFiles()).length;
    }

    private boolean isAllChunksUploaded(String fileName, int totalChunks) {
        // 判断所有切片是否已都上传完成
        List<String> uploadFlags = redisTemplate.opsForList().range("upload:" + fileName, 0, -1);
        return uploadFlags != null && uploadFlags.size() == totalChunks;
    }

    private void sendMergeRequest(String fileName, int totalChunks) {
        // 发送合并文件请求
        new Thread(() -> {
            try {
                URL url = new URL("http://localhost:8080/mergeFile");
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");
                conn.setDoOutput(true);
                conn.setDoInput(true);
                conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
                OutputStream out = conn.getOutputStream();
                String query = "fileName=" + fileName;
                out.write(query.getBytes());
                out.flush();
                out.close();
                BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
                while (br.readLine() != null) ;
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
}

其中,file.upload-path为文件上传的保存路径,可以在application.properties或application.yml中进行配置。同时需要添加RedisTemplate的Bean以便记录上传状态。

RedisTemplate配置

如果需要使用RedisTemplate,需要引入下方的包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

同时在yml配置redis的信息

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database=0

然后在自己的类中这样使用

@Component
public class myClass {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
}

注意事项

  • 需要控制每次上传的切片大小,以兼顾上传速度和稳定性,避免占用过多服务器资源或因网络不稳定而导致上传失败。
  • 切片上传存在先后顺序,需要保证所有切片都上传完成后再进行合并,否则可能会出现文件不完整或者文件合并错误等情况。
  • 上传完成后需要及时清理临时文件,避免因为占用过多磁盘空间而导致服务器崩溃。可以设置一个定期任务来清理过期的临时文件。

结语

到此这篇关于vue+springboot上传大文件的实现示例的文章就介绍到这了,更多相关vue springboot上传大文件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • springboot大文件上传、分片上传、断点续传、秒传的实现

    对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送.接收都是不可取,很容易导致内存问题.所以对于大文件上传,采用切块分段上传,从上传的效率来看,利用多线程并发上传能够达到最大效率. 本文是基于 springboot + vue 实现的文件上传,本文主要介绍服务端实现文件上传的步骤及代码实现,vue的实现步骤及实现请移步本人的另一篇文章 vue 大文件分片上传 - 断点续传.并发上传 上传分步: 本人分析上传总共分为: 检查文件是否已上传,如已上传可实现秒传 创建临时文件(._tmp

  • Spring Boot超大文件上传实现秒传功能

    目录 1.分片上传 1.1 什么是分片上传 1.2 分片上传的场景 2.断点续传 2.1 什么是断点续传 2.2 应用场景 2.3 实现断点续传的核心逻辑 2.4 实现流程步骤 3.分片上传/断点上传代码实现 3.1 前端实现 3.2 后端写入文件 3.3 进行写入操作的核心代码 4.秒传 4.1 什么是秒传 4.2 实现的秒传核心逻辑 4.3 核心代码 5.总结 文件上传是一个老生常谈的话题了,在文件相对比较小的情况下,可以直接把文件转化为字节流上传到服务器,但在文件比较大的情况下,用普通的方

  • SpringBoot 中大文件(分片上传)断点续传与极速秒传功能的实现

    1.创建SpringBoot项目 本项目采用springboot + mybatis-plus +jquery +thymeleaf组成 2.项目流程图 3.在pom中添加以下依赖 <!--lombok依赖--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optio

  • springboot整合vue实现上传下载文件

    springboot整合vue实现上传下载文件,供大家参考,具体内容如下 环境 springboot 1.5.x 完整代码下载:springboot整合vue实现上传下载 1.上传下载文件api文件 设置上传路径,如例子: private final static String rootPath = System.getProperty("user.home")+File.separator+fileDir+File.separator; api接口: 下载url示例:http://l

  • vue+springboot上传文件、图片、视频及回显到前端详解

    目录 效果图 设计逻辑 数据库表 前端vue html js代码 前端思路 储存文件信息 上传文件对象 后端上传下载代码 完整代码 workinfo.vue SubmitHomeworkController 总结 效果图 预览: 设计逻辑 数据库表 前端vue html <div class="right-pannel"> <div class="data-box"> <!--上传的作业--> <div style=&quo

  • SSM框架+Plupload实现分块上传大文件示例

    关于Plupload的介绍,相信它的官网http://www.plupload.com/已经给得很详细了.Plupload的上传原理简单点说,就是将用户选中的文件(可多个)分隔成一个个小块,依次向服务器上传,这是它能驾驭上传大文件的原因之一,而且在这个过程可以暂停上传,暂停后再继续上传(异于断点续传).最重要的是,从头到尾没有一点点UI阻塞,保证了用户体验.下面会开始讲Plupload的实现流程,分析原理,并在最后给出效果图. 在此之前先说说我的项目,做的j2ee项目运用到spring+Spri

  • 基于SpringBoot上传任意文件功能的实现

    一.pom文件依赖的添加 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</gr

  • Java中使用WebUploader插件上传大文件单文件和多文件的方法小结

    一.使用webuploader插件的原因说明 被现在做的项目坑了. 先说一下我的项目架构spring+struts2+mybatis+MySQL 然后呢.之前说好的按照2G上传就可以了,于是乎,用了ajaxFileUpload插件,因为之前用图片上传也是用这个,所以上传附件的时候就直接拿来用了 各种码代码,测试也测过了,2G文件上传没问题,坑来了,项目上线后,客户又要求上传4G文件,甚至还有20G以上的..纳尼,你不早说哦... 在IE11下用ajaxFileUpload.js插件上传超过4G的

  • ASP.NET解决上传大文件问题的方法

    上传文件的控件为:FileUpload Asp.Net对上传文件大小有限制.默认情况下用户只能上传4MB大小的文件,这会给用户带来不便.所以如果要上传40MB大小的文件.只能修改配置文件 关键代码如下 复制代码 代码如下: protected void btnSend_Click(object sender, EventArgs e) { try { //上传文件的思路: //获取上传文件的名称,此时为一个全路径的地址 string upFileName = fulFileName.FileNa

  • FireFox浏览器使用Javascript上传大文件

    本程序是利用3.x的Firefox浏览器可以读取本地文件的特性,实现通过xmlHttPRequest上传大文件功能,并在可以上传过程中动态显示上传进度.略加修改,并与服务器端配合,可以实现断点续传等诸多功能.本例主要是研究FireFox的file-input节点的一些特性,其他客户端应用,如Flash.Sliverlight等,在实现客户端大文件上传时,在数据传输与服务器端存储等方面,与本例的思路基本一致.注意:文件体积似乎有临界点,但这个临界点是多少尚未确认.建议不要用此方法上传超过100M的

  • php上传大文件失败的原因及应对策略

    为什么上传大文件总是失败,但是上传小文件就没有问题.小编也不得其解,网上搜其原因,整理了一篇关于php上传大文件失败的原因和解决办法的文章,分享给大家. 下面分别是各种原因以及解决办法: 第1种情况:文件上传时存放文件的临时目录必须是开启的并且是 PHP 进程所有者用户可写的目录.如果未指定则 PHP 使用系统默认值. php.ini文件中upload_tmp_dir用来说明PHP上传的文件放置的临时目录,要想上传文件,得保证服务器没有关闭临时文件并对该文件夹有写入的权限. 第2种情况:max_

  • asp.net 上传大文件解决方案

    这次在项目中,用到了大文件上传,要上传的文件有100多m,于是研究现在国内使用的大文件上传的组件发现用的比较多的有两个控件AspnetUpload 2.0和Lion.Web.UpLoadModule,另外还有思归在它的博客堂中所说的办法 http://blog.joycode.com/saucer/archive/2004/03/16/16225.aspx   两个控件的方法是:利用隐含的HttpWorkerRequest,用它的GetPreloadedEntityBody 和 ReadEnti

  • Nginx 上传大文件超时解决办法

    Nginx 上传大文件超时解决办法 情况如下:用nginx作代理服务器,上传大文件时(本人测试上传50m的文件),提示上传超时或文件过大. 原因是nginx对上传文件大小有限制,而且默认是1M.另外,若上传文件很大,还要适当调整上传超时时间. 解决方法是在nginx的配置文件下,加上以下配置: client_max_body_size 50m; //文件大小限制,默认1m client_header_timeout 1m; client_body_timeout 1m; proxy_connec

随机推荐