使用AWS SDK实现视频流:第二次字节范围请求失败
问题分析
错误的核心是:你返回的ByteArrayResource是当前从S3获取的2MB块内容,但Spring的ResourceHttpMessageWriter会读取浏览器发送的Range头(bytes=2097152-),并尝试从这个Resource中从2097152的位置开始读取数据。但你的Resource总长度只有2097152字节(索引范围为0到2097151),2097152的位置超出了资源范围,因此触发了IllegalArgumentException。
解决方案
方案1:返回byte[]而非Resource类型
因为Spring对Resource类型的响应会自动处理请求的Range头,但你已经手动从S3获取了正确的块内容,不需要Spring再做切割。将返回类型改为byte[],让Spring直接输出字节内容即可:
服务层代码修改:
EcsObject ecsObject = awsEcsRepository.getObjectByRange(fileName, bucketName, null, contentRange, correlationId); byte[] videoBytes = ecsObject.getInputStream().readAllBytes(); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.valueOf("video/webm")); headers.setContentLength(videoBytes.length); // 设置当前块的实际长度 headers.set(HttpHeaders.CONTENT_RANGE, "bytes " + rangeStart + "-" + rangeEnd + "/" + fileSize); headers.set(HttpHeaders.ACCEPT_RANGES, "bytes"); return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT) .headers(headers) .body(videoBytes);
控制器代码修改:
public ResponseEntity<byte[]> getVideoFromS3(@RequestHeader("Range") String range) { return streamingService.getVideoFromS3(range); }
方案2:调整Range计算并使用InputStreamResource(可选)
如果坚持使用Resource类型,可以改用InputStreamResource,同时修正Range结束值的计算逻辑:2MB块的结束值应为rangeStart + 2097151(因为字节索引从0开始,2MB包含2097152个字节),比如第二次请求的结束值应为2097152 + 2097151 = 4194303(而非你之前写的4194302),避免少返回1字节。
修改后的服务层代码示例:
EcsObject ecsObject = awsEcsRepository.getObjectByRange(fileName, bucketName, null, contentRange, correlationId); InputStreamResource videoResponse = new InputStreamResource(ecsObject.getInputStream()); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.valueOf("video/webm")); headers.setContentLength(ecsObject.getContentLength()); headers.set(HttpHeaders.CONTENT_RANGE, "bytes " + rangeStart + "-" + rangeEnd + "/" + fileSize); headers.set(HttpHeaders.ACCEPT_RANGES, "bytes"); return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT) .headers(headers) .body(videoResponse);
额外注意事项
- 当剩余文件长度不足2MB时,
rangeEnd应设为总文件大小减1(比如最后一次请求,总文件12MB,最后一个块的rangeStart是10485760,rangeEnd是12582911)。 - 验证从S3获取的内容长度与你计算的块长度一致,避免出现长度不匹配的问题。
内容的提问来源于stack exchange,提问作者Shannu Bujji
相关产品推荐
相关产品推荐

