Spring Boot 实现大文件分片上传

Spring Boot 实现大文件分片上传

在 Maven 中添加依赖项

需要在项目中添加以下依赖项:

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
dependency>
 
<dependency>
    <groupId>commons-fileuploadgroupId>
    <artifactId>commons-fileuploadartifactId>
    <version>1.4version>
dependency>

创建控制器类

创建一个控制器类,用于处理上传请求。在该类中,需要实现以下操作:

  • 接收上传的文件
  • 将文件分成多个部分
  • 将每个部分保存到磁盘上的临时文件中
  • 当所有部分都上传完成后,将它们合并成一个完整的文件

下面是一个示例代码:

@RestController
public class FileUploadController {
 
    private static final String UPLOAD_DIRECTORY = "/tmp/uploads";
 
    @PostMapping("/upload")
    public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file,
                                          @RequestParam("fileName") String fileName,
                                          @RequestParam("chunkNumber") int chunkNumber,
                                          @RequestParam("totalChunks") int totalChunks) throws IOException {
        
        
        File uploadDirectory = new File(UPLOAD_DIRECTORY);
        if (!uploadDirectory.exists()) {
            uploadDirectory.mkdirs();
        }
 
        File destFile = new File(UPLOAD_DIRECTORY + File.separator + fileName + ".part" + chunkNumber);
        FileUtils.copyInputStreamToFile(file.getInputStream(), destFile);
 
        if (chunkNumber == totalChunks) { // 如果所有部分都已上传,则将它们组合成一个完整的文件
            String targetFilePath = UPLOAD_DIRECTORY + File.separator + fileName;
            for (int i = 1; i <= totalChunks; i++) {
                File partFile = new File(UPLOAD_DIRECTORY + File.separator + fileName + ".part" + i);
                try (FileOutputStream fos = new FileOutputStream(targetFilePath, true)) {
                    FileUtils.copyFile(partFile, fos);
                    partFile.delete();
                }
            }
        }
 
        return ResponseEntity.ok("Upload successful");
    }
}

前端实现

在前端,需要使用 JavaScript 将文件分割成多个部分,并向后端发送分块数据。以下是一个示例代码:

javascript复制代码function uploadFile(file) {
    const chunkSize = 1024 * 1024; // 每个部分的大小(1MB)
    const totalChunks = Math.ceil(file.size / chunkSize); // 总部分数
 
    let currentChunk = 1;
    let startByte = 0;
 
    while (startByte < file.size) { // 分割文件为多个部分,并上传每个部分
        const endByte = Math.min(startByte + chunkSize, file.size);
        const chunk = file.slice(startByte, endByte);
 
        const formData = new FormData();
        formData.append('file', chunk);
        formData.append('fileName', file.name);
        formData.append('chunkNumber', currentChunk);
        formData.append('totalChunks', totalChunks);
 
        axios.post('/upload', formData);
 
        startByte += chunkSize;
        currentChunk++;
    }
}

通过以上步骤,就可以使用 Spring Boot 实现大文件分片上传了。

第二种:

package cn.js.Controller;

import org.apache.commons.io.FileUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

 /**
   *@description:
  * 1.前端实现:
  *
  * 使用JavaScript实现前端上传代码。从前端发送请求时,将文件切成若干个小块(每个块大小在1MB到10MB之间),
  * 并发送给后端。当所有的块都上传完成后,再向后端发送“合并”请求。
  *
  * 2.后端实现:
  *
  * 在后端控制器中,首先接收上传的文件,并按照指定的大小进行分块。然后将每一个块存储为临时文件。
  * 当所有块都上传完成后,再将这些临时文件合并成一个完整的文件。
  *
  * 具体实现方法如下:
   **/


  /**
    *@description:
   * 在上述代码中,我们定义了一个接口“/file/upload”,用于接收上传的块。在接收到一个块时,
   * 首先判断是否为第一个块,如果是,则新建一个空文件。然后将当前块写入文件。
   * 如果是最后一个块,则调用 mergeFile() 方法将所有块合并成一个完整的文件。
   *
   * mergeFile() 方法中,我们首先获取所有以 guid 为前缀的临时文件,
   * 并按照文件名排序。然后从第一个文件开始,将每个文件的内容写入目标文件中,
   * 并删除临时文件。最后输出合并完成的消息。
   *
   * 这样,就可以使用Spring Boot实现大文件分片上传了。需要注意的是,
   * 为了防止文件名冲突,可以为每个上传任务生成一个唯一的 guid,并使用该 guid
   * 作为文件名的前缀。此外,如果上传任务长时间未完成,还需要定期清理临时文件。
    **/
@RestController
@RequestMapping("/file")
public class FileUploadController {

    private final String UPLOAD_PATH = "D:/upload/";

    @PostMapping("/upload")
    public String upload(HttpServletRequest request) throws Exception {
        String fileName = request.getHeader("fileName");
        String guid = request.getHeader("guid");
        int chunkIndex = Integer.parseInt(request.getHeader("chunkIndex"));//分片文件的索引从0开始
        int totalChunks = Integer.parseInt(request.getHeader("totalChunks"));//分片文件的索引总数

        // 获取上传的文件
        File file = new File(UPLOAD_PATH + fileName);

        // 如果是第一个块,则新建一个文件
        if (chunkIndex == 0) {
            file.createNewFile();
        }

        // 将当前块写入文件
        FileOutputStream fos = new FileOutputStream(file, true);
        InputStream is = request.getInputStream();
        byte[] buf = new byte[1024];
        int len;
        while ((len = is.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }

        // 如果是最后一个块,则合并文件
        if (chunkIndex == totalChunks - 1) {
            mergeFile(file, guid);
        }

        

        return "success";
    }

    private void mergeFile(File file, String guid) throws Exception {
        String fileName = file.getName();
        String ext = fileName.substring(fileName.lastIndexOf("."));
        File newFile = new File(UPLOAD_PATH + guid + "." + ext);

        List<File> files = Arrays.asList(file.getParentFile().listFiles((dir, name) -> name.startsWith(guid)));
        Collections.sort(files, Comparator.comparing(File::getName));

        FileOutputStream fos = new FileOutputStream(newFile);
        byte[] buf = new byte[1024];
        int len;
        for (File f : files) {
            FileInputStream fis = new FileInputStream(f);
            while ((len = fis.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }
            fis.close();
            f.delete();
        }
        fos.close();

        System.out.println("文件合并完成:" + newFile.getAbsolutePath());
    }
}

你可能感兴趣的:(SpringBoot,spring,boot,java,后端)