java实现文件上传、分片下载、删除(safire浏览器和苹果手机无法播放视频音频问题)
目录原理说明:代码部分说明文件上传主要方法:文件删除代码:主要介绍文件分片下载:记录原因:发现safire浏览器和苹果手机请求数据时,它不是一次性请求完文件流,他会先发送一个类似跨域的预请求,先请求1个字节的数据,后续进行分段获取文件流,所以,要实现这一需求必须做分片下载git地址,可以参考文件处理代码,具体逻辑根据自己项目需求进行更改:https://githu...
目录
记录原因:发现safire浏览器和苹果手机请求数据时,它不是一次性请求完文件流,他会先发送一个类似跨域的预请求,先请求1个字节的数据,后续进行分段获取文件流,所以,要实现这一需求必须做分片下载
git地址,可以参考文件处理代码,具体逻辑根据自己项目需求进行更改:https://github.com/pengxingjia/file-uplaod-download.git
原理说明:
- 普通的类似安卓、谷歌浏览器下载都是一次性请求完所有数据,但是safire和苹果手机不是,是分片下载(后面统称苹果)
- 苹果请求时,请求头中会携带range,第一次请求携带的是Range: bytes=0-1,后端需要返回本次请求的字节数
Content-length:"本次请求的字节数",
Content-Range:bytes=该次请求字节的起始位置-该次请求字节数的结束位置/文件总字节数; -
分片请求返回的状态码固定为206
-
Content-Disposition如果没有设置这个请求头,则Safire浏览器点击下载会直接进入预览模式,设置之后,点击下载后会直接下载图片文件
-
对应音频视频文件,需要对请求头做特殊处理,如下
-
//音视频格式,不区分大小写 private static List<String> extNameVideoList; private static List<String> extNameAudioList; private void initExtNameMap(){ if (extNameVideoList == null) { extNameVideoList = new ArrayList<>(); extNameVideoList.add("mp4"); extNameVideoList.add("mov"); } if (extNameAudioList == null){ extNameAudioList = new ArrayList<>(); extNameAudioList.add("mp3"); } } /** * 判断后缀是否是音频/视频类设置响应的响应头 * * @param extName 后缀名 */ private void checkExtNameSetHealer(HttpServletResponse response, String extName) { initExtNameMap(); String lowExtUser = extName.toLowerCase(); if (extNameVideoList.contains(lowExtUser)){ response.setContentType("video/mp4"); response.setHeader("Content-Type", "video/mp4"); }else if (extNameAudioList.contains(lowExtUser)){ response.setContentType("audio/mp3"); response.setHeader("Content-Type", "audio/mp3"); } }
对应视频文件,请求头需要设置为:
response.setContentType("video/mp4");
response.setHeader("Content-Type", "video/mp4");
对于音频文件,请求头需设置为:
response.setContentType("audio/mp3");
response.setHeader("Content-Type", "audio/mp3");
-
代码部分说明
文件上传主要方法:
1.先解析出文件的名字、后缀
2.fileName用uuid是为了存储到磁盘时文件名唯一
3.具体参考项目代码
@Override
public FileInfoDto fileUpload(HttpServletRequest request, MultipartFile file) {
String fileName = UuidUtils.getUuid();
String originalName = file.getOriginalFilename();
String extName = "";
if (originalName != null && originalName.contains(".")) {
extName = originalName.substring(originalName.lastIndexOf(".") + 1);
fileName = fileName + "." + extName;
}
//构建空文件对象
File newFile = FileUtil.getFilePath(fileName);
//将文件写入到空文件对象中
try {
file.transferTo(newFile);
} catch (IOException e) {
log.error(e.getMessage());
throw new FileException(ErrorCodeConstants.FILE_TRANSFER_EXCEPTION);
}
//保存文件关联关系
FileInfo fileInfo = initInfoFileRel(request, file, fileName, extName);
return this.getFileInfoDto(fileInfo);
}
文件删除代码:
1.通过地址获取文件对象,若文件对象存在,则调用file.delete()进行删除
@Override
public void fileDelete(String url) {
File file = FileUtil.getFilePath(url);
if (file.exists()){
if (file.delete()){
log.error("文件删除失败");
}
}
}
主要介绍文件分片下载:
- 此方法设置公共的参数,根据请求方式选择是一次性下载还是分片下载
//苹果代表分片下载,其他代表一次性下载文件
public void sendVideo(HttpServletRequest request, HttpServletResponse response, File file, String originalName, String extName) throws IOException {
//判断后缀是否是音频/视频类
checkExtNameSetHealer(response, extName);
response.addHeader("Content-Disposition", "attachment;filename=\"" + new String(originalName.getBytes(StandardCharsets.UTF_8), "iso8859-1") + "\"");
response.setHeader("ETag", originalName);
response.setHeader("Last-Modified", new Date().toString());
response.setHeader("Accept-Ranges", "bytes");
//设置文件为只读模式
RandomAccessFile randomFile = new RandomAccessFile(file, "r");
long contentLength = randomFile.length();
if (contentLength > fileMaxSize){
log.error("文件太大,最大支持2g");
throw new RuntimeException("文件太大");
}
response.setHeader("Content-length", "" + contentLength);
//分片下载时的请求头Range
String range = request.getHeader("Range");
try {
if (range != null && range.startsWith("bytes=")) {
//分段读取文件流
pieceDownFileInfo(range, response, randomFile);
} else {
//一次性读取所有文件流
readAllAtOnce(response, randomFile);
}
}catch (IOException io){
io.getStackTrace();
}
}
- 算了 建议看代码吧,文档反而越说越模糊
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)