前几天写了一篇博客几种排序的不同写法 和 遍历列表的另外形式,但是在实际应用中还是犯了错误,就跟梗图里面表达的意思简直一样。
刚写完就出错,实在是有点尴尬。
场景:正在做一个视频文件上传的模块,前端点击上传后传到后端,有四个步骤。
- 文件上传前的准备工作,校验文件是否存在;
- 检查分块文件是否存在;
- 上传分块;
- 合并分块。
功能完成后点击上传,发现分块目录、分块文件和合并文件都成功完成生成,但是前端就是显示上传失败!
通过断点调试发现,问题就出在第三步,合并分块后,对合并文件和原文件的Md5值的校验上。合并后的文件和原文件的Md5值不一样! 虽然两个文件的大小一模一样,但是Md5不一样就说明两个文件的内容已经改变了。此时再看合并文件,发现不能打开!
省略中间漫长的查找BUG过程,直接说结果。上一段代码吧。
private File mergeFile(List<File> chunkFileList, File mergeFile) {
// 对块文件进行排序
chunkFileList.sort(Comparator.comparing(File::getName));
// 创建一个写对象
try (RandomAccessFile raf_write = new RandomAccessFile(mergeFile, "rw")) {
byte[] b = new byte[1024];
for (File chunkFile : chunkFileList) {
// 创建一个读对象
try (RandomAccessFile raf_read = new RandomAccessFile(chunkFile, "r")) {
int len = -1;
while ((len = raf_read.read(b)) != -1) {
raf_write.write(b, 0, len);
}
}
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
return mergeFile;
}
注意这一段代码:
chunkFileList.sort(Comparator.comparing(File::getName));
最终定位,问题就是这一段代码。方法引用表达式里面,getName
返回的是String
类型的文件名,这里的比较器比较的是String类型的name。而根据自己生成分块的逻辑,是以int类型的0开头,从大到小的分块名。下面进行一下这一段的测试。
@Test
public void test11() {
// 分块文件夹路径
String chunkFileFolderPath = "E:\\webs_workspace\\video\\3\\d\\3dcf73efe37de6608e3a2c48b8f6fbe1\\chunks\\";
// 分块文件夹
File chunkFileFolder = new File(chunkFileFolderPath);
File[] files = chunkFileFolder.listFiles();
assert files != null;
List<File> fileList = Arrays.asList(files);
fileList.sort(Comparator.comparing(File::getName));
fileList.forEach(System.out::println);
}
控制台输出:
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\0
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\1
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\10
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\11
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\12
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\13
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\14
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\15
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\16
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\17
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\18
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\19
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\2
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\20
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\21
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\22
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\23
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\24
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\25
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\26
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\27
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\28
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\29
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\3
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\4
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\5
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\6
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\7
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\8
E:\webs_workspace\video\3\d\3dcf73efe37de6608e3a2c48b8f6fbe1\chunks\9
进程已结束,退出代码0
现在显而易见,chunkFileList.sort(Comparator.comparing(File::getName));
这一段,在比较name
字段时,是将其作为字符串比较的。先比较第一个字节,然后比较第二个,以此类推。所以就会出现上面那个数完1后直接开始10,19数完了数2的奇怪局面。当然,排序没排好就会导致分块列表不正确,进而导致块文件合并顺序与分割顺序不一致。
最后是解决,换成了λ表达式就可以了。
// 对块文件进行排序
chunkFileList.sort((o1, o2) -> {
return Integer.parseInt(o1.getName()) > Integer.parseInt(o2.getName()) ? 1 : -1;
});
也尝试过用方法引用表达式来完成,但是没有找到很好的办法将name转为int类型。