需求:客户端读取视频资源,边下载边读取
适用:minio文件服务器或其它本地服务器,oss文件服务器本身就支持,不需要此类方法
已解决:ios系统在资源过大的情况会出现异常。
public void getFileForRange(@PathVariable("fileId") String fileId,
OutputStream out,HttpServletRequest request, HttpServletResponse response) throws IOException {
String filepath = String.format("%s/%s","存储文件路径",fileId);
FileInputStream inputFileStream = null;
BufferedOutputStream outputStream = null;
try {
File file = new File(filepath);
if (!file.exists()) {
// 禁止多线程同时下载视频文件
try {
if(redisUtils.tryLock(CacheKey.VIDEO_CACHE_KEY + fileId)){
//mino获取文件并写在服务端
InputStream stream = fileBiz.get(fileId);
cn.hutool.core.io.FileUtil.writeFromStream(stream, file);
}
}catch (Exception e){
log.error("下载文件失败:{}",e);
}finally {
redisUtils.release(CacheKey.VIDEO_CACHE_KEY + fileId);
}
}
long point = 0;
long fileLength = file.length();
long position_end = fileLength;
inputFileStream = new FileInputStream(file);
outputStream = new BufferedOutputStream(response.getOutputStream());
response.reset();
//设置支持断点
response.setHeader("Accept-Ranges", "bytes");
//获取断点位置
String Range = request.getHeader("Range");
if (Range != null) {
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
String RangeFromTo = Range.replaceAll("bytes=", "");
log.info("threadName: " + Thread.currentThread().getId() + " Range : " + RangeFromTo);
String[] Ranges = RangeFromTo.split("-");
point = Long.parseLong(Ranges[0]);
if (2 == Ranges.length && !Ranges[1].equals("")) {
position_end = Long.parseLong(Ranges[1]);
}
}
//写明要下载的文件的大小
response.setHeader("Content-Length", Long.toString(position_end - point));
log.info("threadName: " + Thread.currentThread().getId() + "point : " + point + " Content-Length : " + (position_end - point));
//设置response的编码方式
response.setContentType("application/x-msdownload");
//设置断点续传回应头
String contentRange = new StringBuffer("bytes ")
.append(point)
.append("-")
.append(fileLength - 1)
.append("/")
.append(fileLength)
.toString();
if (point != 0) {
// 断点续传的回应头:告诉改块插入的位置和文件的总大小
// 格式:Content-Range: bytes [文件块的开始字节]-[文件块的结束字节 - 1]/[文件的总大小]
contentRange = new StringBuffer("bytes ")
.append(point)
.append("-")
.append(position_end - 1)
.append("/")
.append(fileLength)
.toString();
// 移动文件指针位置,断点处
inputFileStream.skip(point);
}
response.setHeader("Accept-Ranges", "bytes");
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
response.setContentType("video/mp4");
// ios 默认不为添加
response.setHeader("Content-Type", "video/mp4");
response.setHeader("Content-Disposition", "inline;filename=" + filepath);
response.setHeader("Content-Length", String.valueOf(position_end - point + 1));
response.setHeader("Content-Range", "bytes " + position_end + "-" + point + "/" + file.length());
response.setHeader("Content-Range", contentRange);
long bytesWritten = 0;
byte[] bytes = new byte[1024 * 4];
int byteCount;
long NeedWriten = position_end - point + 1;
while (NeedWriten >= bytesWritten && (byteCount = inputFileStream.read(bytes)) != -1) {
if (NeedWriten >= bytesWritten) {
long tTempWriten = (bytesWritten + byteCount) > NeedWriten ? (NeedWriten - bytesWritten) : byteCount;
outputStream.write(bytes, 0, (int) tTempWriten);
bytesWritten += tTempWriten;
}
}
} catch (FileNotFoundException e) {
log.error("读取文件失败:" + filepath + " FileNotFoundException happen : " + e + " URL : " + request.getRequestURL().toString());
} catch (IOException e) {
log.error("读取文件失败:" + filepath + " IOException hapepn : " + e + " URL : " + request.getRequestURL().toString());
} finally {
try {
if (null != inputFileStream) {
inputFileStream.close();
}
if (null != outputStream) {
outputStream.close();
}
} catch (IOException e) {
log.error("下载文件失败:" + filepath + " EofException happen : " + e);
}
}
}
参考:Java断点下载_ZeroKoop的博客-CSDN博客