本文将介绍如何使用Springboot和Vue3实现断点上传下载,包括暂停上传、恢复上传、暂停下载和恢复下载功能。

  1. 后端实现

首先,我们需要在Springboot中创建一个Controller来处理上传和下载请求。在这个Controller中,我们需要实现以下功能:

  • 断点上传:当上传文件时,如果中途出现网络问题或者用户中断了上传过程,我们需要能够在之后的上传中恢复之前的进度。
  • 暂停上传:当用户想要暂停上传时,我们需要能够停止上传,并记录当前上传进度。
  • 恢复上传:当用户想要恢复上传时,我们需要能够从之前的进度继续上传。
  • 断点下载:当下载文件时,如果中途出现网络问题或者用户中断了下载过程,我们需要能够在之后的下载中恢复之前的进度。
  • 暂停下载:当用户想要暂停下载时,我们需要能够停止下载,并记录当前下载进度。
  • 恢复下载:当用户想要恢复下载时,我们需要能够从之前的进度继续下载。

以下是一个简单的Controller示例:

@RestController
public class FileController {
    private static final String FILE_PATH = "/path/to/files/";

    @PostMapping("/upload")
    public ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
        // 获取上传文件的MD5值
        String md5 = request.getHeader("md5");
        // 获取上传文件的起始位置
        long start = Long.parseLong(request.getHeader("start"));
        // 获取上传文件的总长度
        long total = Long.parseLong(request.getHeader("total"));
        // 获取上传文件的文件名
        String fileName = request.getHeader("fileName");

        // 生成上传文件的路径
        String filePath = FILE_PATH + fileName;

        // 检查文件是否已经存在
        File fileToSave = new File(filePath);
        if (fileToSave.exists()) {
            return ResponseEntity.status(HttpStatus.CONFLICT).build();
        }

        // 如果文件不存在,则创建文件
        try {
            fileToSave.createNewFile();
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }

        // 上传文件
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(fileToSave, "rw")) {
            // 将文件指针移动到指定的位置
            randomAccessFile.seek(start);

            // 将文件流中的数据写入文件
            byte[] buffer = new byte[1024];
            int len;
            while ((len = file.getInputStream().read(buffer)) != -1) {
                randomAccessFile.write(buffer, 0, len);
            }

            // 如果文件已经上传完毕,则返回成功
            if (start + file.getSize() == total) {
                return ResponseEntity.ok().build();
            }

            // 如果文件还没有上传完毕,则返回文件上传进度
            HttpHeaders headers = new HttpHeaders();
            headers.add("Content-Range", "bytes " + (start + file.getSize()) + "-" + (total - 1) + "/" + total);
            return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).headers(headers).build();
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    @GetMapping("/download")
    public ResponseEntity<?> downloadFile(HttpServletRequest request, HttpServletResponse response) {
        // 获取下载文件的MD5值
        String md5 = request.getHeader("md5");
        // 获取下载文件的起始位置
        long start = Long.parseLong(request.getHeader("start"));
        // 获取下载文件的结束位置
        long end = Long.parseLong(request.getHeader("end"));
        // 获取下载文件的文件名
        String fileName = request.getHeader("fileName");

        // 生成下载文件的路径
        String filePath = FILE_PATH + fileName;

        // 检查文件是否存在
        File fileToDownload = new File(filePath);
        if (!fileToDownload.exists()) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }

        // 设置响应头
        response.setHeader("Content-Type", "application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        // 下载文件
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(fileToDownload, "r")) {
            // 将文件指针移动到指定的位置
            randomAccessFile.seek(start);

            // 将文件流中的数据写入响应流
            byte[] buffer = new byte[1024];
            int len;
            while ((len = randomAccessFile.read(buffer)) != -1) {
                response.getOutputStream().write(buffer, 0, len);
                end -= len;
                if (end < 0) {
                    break;
                }
            }
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }

        return ResponseEntity.ok().build();
    }
}
  1. 前端实现

接下来,我们需要在Vue3中实现上传和下载功能,并添加暂停和恢复功能。我们可以使用Axios库来处理上传和下载请求,并使用Vue3的Composition API来管理上传和下载状态。

以下是一个简单的Vue3组件示例:

<template>
  <div>
    <input type="file" @change="selectFile" />
    <button @click="startUpload">Upload</button>
    <button @click="pauseUpload">Pause</button>
    <button @click="resumeUpload">Resume</button>
    <button @click="startDownload">Download</button>
    <button @click="pauseDownload">Pause</button>
    <button @click="resumeDownload">Resume</button>
  </div>
</template>

<script>
import { ref } from "vue";
import axios from "axios";

export default {
  setup() {
    // 上传状态
    const uploadStatus = ref("idle");
    const uploadProgress = ref(0);

    // 下载状态
    const downloadStatus = ref("idle");
    const downloadProgress = ref(0);

    // 选择文件
    const selectFile = (event) => {
      file.value = event.target.files[0];
    };

    // 开始上传
    const startUpload = async () => {
      if (!file.value) {
        return;
      }

      const url = "/upload";
      const md5 = await calculateMD5(file.value);
      const total = file.value.size;
      let start = 0;
      let headers = {
        md5,
        total,
        fileName: file.value.name,
      };

      while (start < total) {
        if (uploadStatus.value === "paused") {
          return;
        }

        headers.start = start;
        let blob = file.value.slice(start, Math.min(start + CHUNK_SIZE, total));
        let formData = new FormData();
        formData.append("file", blob, file.value.name);

        try {
          let response = await axios.post(url, formData, {
            headers,
            onUploadProgress: (event) => {
              uploadProgress.value = Math.round((start + event.loaded) / total * 100);
            },
          });

          if (response.status === 200) {
            uploadStatus.value = "done";
            break;
          } else if (response.status === 206) {
            start = parseInt(response.headers["content-range"].split("-")[1]) + 1;
          }
        } catch (error) {
          uploadStatus.value = "error";
          break;
        }
      }
    };

    // 暂停上传
    const pauseUpload = () => {
      uploadStatus.value = "paused";
    };

    // 恢复上传
    const resumeUpload = () => {
      uploadStatus.value = "uploading";
      startUpload();
    };

    // 开始下载
    const startDownload = async () => {
      const url = "/download";
      const md5 = await calculateMD5(file.value);
      const total = file.value.size;
      let start = 0;
      let end = CHUNK_SIZE - 1;
      let headers = {
        md5,
        total,
        fileName: file.value.name,
      };

      while (start < total) {
        if (downloadStatus.value === "paused") {
          return;
        }

        headers.start = start;
        headers.end = end;

        try {
          let response = await axios.get(url, {
            headers,
            responseType: "blob",
            onDownloadProgress: (event) => {
              downloadProgress.value = Math.round((start + event.loaded) / total * 100);
            },
          });

          let blob = response.data;
          let fileReader = new FileReader();
          fileReader.readAsArrayBuffer(blob);

          fileReader.onload = () => {
            let arrayBuffer = fileReader.result;
            let data = new Uint8Array(arrayBuffer);
            let blob = new Blob([data]);
            let url = window.URL.createObjectURL(blob);
            let a = document.createElement("a");
            a.href = url;
            a.download = file.value.name;
            a.click();
            window.URL.revokeObjectURL(url);
          };

          if (response.status === 200) {
            downloadStatus.value = "done";
            break;
          } else if (response.status === 206) {
            start = parseInt(response.headers["content-range"].split("-")[1]) + 1;
            end = Math.min(start + CHUNK_SIZE - 1, total - 1);
          }
        } catch (error) {
          downloadStatus.value = "error";
          break;
        }
      }
    };

    // 暂停下载
    const pauseDownload = () => {
      downloadStatus.value = "paused";
    };

    // 恢复下载
    const resumeDownload = () => {
      downloadStatus.value = "downloading";
      startDownload();
    };

    // 计算文件的MD5值
    const calculateMD5 = async (file) => {
      return new Promise((resolve, reject) => {
        let fileReader = new FileReader();
        fileReader.readAsArrayBuffer(file);

        fileReader.onload = () => {
          let arrayBuffer = fileReader.result;
          let data = new Uint8Array(arrayBuffer);
          let md5 = CryptoJS.MD5(data).toString();
          resolve(md5);
        };

        fileReader.onerror = () => {
          reject();
        };
      });
    };

    const CHUNK_SIZE = 1024 * 1024; // 1MB
    const file = ref(null);

    return {
      uploadStatus,
      uploadProgress,
      downloadStatus,
      downloadProgress,
      selectFile,
      startUpload,
      pauseUpload,
      resumeUpload,
      startDownload,
      pauseDownload,
      resumeDownload,
    };
  },
};
</script>

在这个组件中,我们使用了以下技术:

  • Axios库:用于处理上传和下载请求。
  • Composition API:用于管理上传和下载状态。
  • FileReader API:用于计算文件的MD5值。
  • CryptoJS库:用于计算文件的MD5值。
  1. 结论

使用Springboot和Vue3实现断点上传下载、暂停上传、恢复上传、暂停下载和恢复下载功能并不难。只需要在后端Controller中实现文件上传和下载的断点续传功能,并在前端Vue3组件中使用Axios库和Composition API来管理上传和下载状态即可。这个功能可以为用户提供更好的上传和下载体验,避免因为网络问题或者用户中断导致上传和下载失败的情况

Springboot + Vue3 实现断点上传下载 暂停上传 恢复上传 暂停下载 恢复下载 详细案例

原文地址: https://www.cveoy.top/t/topic/cgFX 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录