Spring Boot中实现大文件分片下载和断点续传功能

在Spring Boot中实现大文件分片下载和断点续传功能,需结合HTTP Range协议和文件流处理技术。以下是完整实现方案:


​​一、核心原理​​

  1. ​HTTP Range协议​
    通过Range请求头指定下载范围(如Range: bytes=0-999),服务器返回206 Partial Content状态码及对应内容片段。
  2. ​断点续传机制​
    客户端记录已下载的字节范围,网络中断后通过Range头续传未完成部分。

​​二、服务端实现​​

​​1. 分片下载接口​​

ini 复制代码
@GetMapping("/download")
public ResponseEntity<InputStreamResource> downloadFile(
    @RequestHeader(value = "Range", required = false) String rangeHeader,
    HttpServletRequest request) throws IOException {
    
    // 获取文件信息
    File file = new File("/path/to/largefile.zip");
    long fileSize = file.length();
    
    // 解析Range头
    long start = 0, end = fileSize - 1;
    if (rangeHeader != null) {
        String[] ranges = rangeHeader.replace("bytes=", "").split("-");
        start = Long.parseLong(ranges[0]);
        if (ranges.length > 1) {
            end = Long.parseLong(ranges[1]);
        }
    }
    
    // 校验范围合法性
    if (start >= fileSize || end >= fileSize || start > end) {
        return ResponseEntity.status(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE)
                .header("Content-Range", "bytes */" + fileSize)
                .build();
    }
    
    // 设置响应头
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    headers.setContentLength(end - start + 1);
    headers.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileSize);
    headers.setHeader("Accept-Ranges", "bytes");
    
    // 返回分片数据流
    try (InputStream is = new FileInputStream(file);
         InputStreamResource resource = new InputStreamResource(is)) {
        is.skip(start);
        return new ResponseEntity<>(resource, headers, HttpStatus.PARTIAL_CONTENT);
    }
}

​​2. 断点续传支持​​

  • ​客户端记录进度​ :保存已下载的字节范围(如0-999)。
  • ​续传请求​ :重新发起请求时携带Range: bytes=1000-,服务器从断点处继续传输。

​​三、客户端实现(JavaScript示例)​​

javascript 复制代码
// 分片下载逻辑
async function downloadFileWithResume(fileId, fileName) {
  const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB
  let downloadedBytes = 0;
  
  // 获取文件总大小
  const response = await fetch(`/api/files/${fileId}/size`);
  const totalSize = parseInt(await response.text());
  
  // 检查本地是否有已下载部分
  const tempFile = await checkLocalProgress(fileName);
  if (tempFile) {
    downloadedBytes = tempFile.size;
  }
  
  // 分片下载
  while (downloadedBytes < totalSize) {
    const end = Math.min(downloadedBytes + CHUNK_SIZE - 1, totalSize - 1);
    const response = await fetch(`/api/files/${fileId}/download`, {
      headers: { Range: `bytes=${downloadedBytes}-${end}` }
    });
    
    if (!response.ok) throw new Error("下载失败");
    
    const blob = await response.blob();
    await appendToFile(tempFile, blob);
    downloadedBytes += blob.size;
    
    // 更新进度
    updateProgress(downloadedBytes / totalSize);
  }
  
  // 完成后重命名文件
  await renameTempFile(tempFile, fileName);
}

// 检查本地进度
async function checkLocalProgress(fileName) {
  const tempPath = `/temp/${fileName}.part`;
  return new Promise(resolve => {
    fs.stat(tempPath, (err, stats) => {
      if (err) resolve(null);
      else resolve(fs.createWriteStream(tempPath, { flags: 'a' }));
    });
  });
}

​​四、关键优化点​​

  1. ​​内存管理​ ​

    使用InputStream流式传输,避免大文件加载到内存。

  2. ​​并发控制​ ​

    通过线程池限制同时下载的分片数:

    arduino 复制代码
    @Service
    public class DownloadService {
        private final ExecutorService executor = Executors.newFixedThreadPool(10);
        
        public void downloadChunk(String fileId, long start, long end) {
            executor.submit(() -> {
                // 分片下载逻辑
            });
        }
    }
  3. ​​进度存储​ ​

    使用Redis记录每个文件的下载进度:

    typescript 复制代码
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public void saveProgress(String fileId, long downloadedBytes) {
        redisTemplate.opsForValue().set(fileId, String.valueOf(downloadedBytes));
    }

​​五、测试方案​​

  1. ​​正常下载​​

    bash 复制代码
    curl -H "Range: bytes=0-999" http://localhost:8080/download
  2. ​​断点续传​ ​

    中断后重新发起相同请求,验证是否从断点续传。

  3. ​​多线程下载​ ​

    使用工具(如Postman)模拟多线程分片请求。


​​六、扩展场景​​

  • ​视频流播放​ :通过Content-Range实现视频边下边播。
  • ​大文件秒传​:基于文件MD5校验,避免重复传输。
  • ​分布式存储​:结合MinIO/S3实现分片存储与合并。

通过上述方案,可高效实现大文件的分片下载和断点续传,支持弱网环境和超大文件传输。完整代码示例可参考GitHub仓库(需替换实际存储路径)。

相关推荐
code2cat3 分钟前
【随笔】RAG语义缓存:问题很相似,答案就能直接复用吗
java·缓存·知识库·rag
乘风gg6 分钟前
出事了,AI 提效后,老板让我裁员20%
前端·后端·ai编程
Rain的Java大神之路25 分钟前
面了 50 个候选人,90% 都挂在 LeetCode 3 无重复字符的最长子串:滑动窗口的 5 个致命坑
java·后端·leetcode
西凉的悲伤27 分钟前
Jsoup 库 HTML 解析详解
java·前端·html·jsoup
RuoyiOffice31 分钟前
SpringBoot OA推荐:面向信创选型,看看这套企业协同平台
spring boot·vue3·信创·flowable·企业协同·ruoyi office·oa选型
十碗饭吃不饱38 分钟前
Apache POI 踩坑:setFontFamily 二次设置字体不生效、样式残留问题深度解析
java·word·apache
jianxuan41 分钟前
Spring Boot + Maven 接入 Allatori:从普通打包到可选混淆发布
spring boot·python·maven
我命由我1234542 分钟前
6-2.Python 异常处理 - 异常捕获(捕获指定异常、常见异常类型、异常信息)
java·运维·开发语言·python·http·https·java-ee
狂师1 小时前
夯爆了!分享几个我在用的,基于业务场景接口串联测试Skill !
人工智能·后端·测试
北京地铁1号线1 小时前
后端开发:缓存(Cache)简介
后端·缓存·cache