2025年5月18日 星期日 乙巳(蛇)年 二月廿 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Java

通过NIO分割文件和合并文件和计算文件md5

时间:02-08来源:作者:点击数:50

最新补充:发现把文件映射为MapedByteBuffer后,即使输入流关闭,通道关闭,它还是引用着文件句柄,这样我们无法删除文件或者重命名,网上有一些解决方法,但是不推荐使用,必竟不是官方出来的方法,所以在官方解决这个问题之前,还是使用普通的方式吧,如下:

  • object FileUtil {
  • /** 获取文件md5(异步方式) */
  • fun getFileMd5(file: File, callback: (String) -> Unit) {
  • thread {
  • val fileMd5 = getFileMd5(file)
  • callback(fileMd5)
  • }
  • }
  • /** 获取文件md5(同步方式) */
  • fun getFileMd5(file: File): String {
  • val messageDigest = MessageDigest.getInstance("MD5")
  • FileInputStream(file).use { fis ->
  • val buf = ByteArray(8192)
  • var length: Int
  • while (fis.read(buf, 0, 8192).also { length = it } != -1) {
  • messageDigest.update(buf, 0, length)
  • }
  • }
  • // 获取md5签名(16个字节)
  • val md5Bytes = messageDigest.digest()
  • // 将byte数组的签名转换为16进制的字符串
  • val bigInteger = BigInteger(1, md5Bytes) // 把16个字节当成一个无符号的大整数
  • var md5String = bigInteger.toString(16) // 把大整数转换为16进制的字符串形式
  • repeat(32 - md5String.length) { // 预防字符串不够32位。1个byte需要两位16进制数,而md5Bytes的长度为16,所以需要32位的16进制数来表示。
  • md5String = "0$md5String"
  • }
  • return md5String
  • }
  • }

NIO就是香啊,不但效率高,而且写起来代码也少(注:下面获取md5的代码不要采用,有Bug),示例如下:

  • object FileUtil {
  • fun getFileMd5(file: File): String {
  • // 获取md5签名
  • val md5Bytes = FileInputStream(file).channel.use { channel ->
  • val byteBuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size())
  • MessageDigest.getInstance("MD5").run {
  • update(byteBuffer)
  • digest()
  • }
  • }
  • // 将签名转换为16进制的字符串
  • val bigInteger = BigInteger(1, md5Bytes) // 把16个字节当成一个无符号的大整数
  • var md5String = bigInteger.toString(16) // 把大整数转换为16进制的字符串形式
  • repeat(32 - md5String.length) { // 预防字符串不够32位。1个byte需要两位16进制数,而md5Bytes的长度为16,所以需要32位的16进制数来表示。
  • md5String = "0$md5String"
  • }
  • return md5String
  • }
  • fun splitFile(file: File, splitCount: Int): MutableList<File> {
  • val splitFiles = mutableListOf<File>()
  • val fileLength = file.length()
  • val avgSize = fileLength / splitCount
  • val lastPartSize = fileLength - avgSize * (splitCount - 1)
  • FileInputStream(file).channel.use { wavChannel ->
  • for (i in 0 until splitCount) {
  • val splitFile = File("${file.absolutePath}.$i")
  • splitFiles.add(splitFile)
  • FileOutputStream(splitFile).channel.use {
  • val position = i * avgSize
  • val count = if (i == splitCount - 1) lastPartSize else avgSize
  • wavChannel.transferTo(position, count, it)
  • }
  • }
  • }
  • return splitFiles
  • }
  • fun mergeFiles(files: List<File>, targetFile: File) {
  • FileOutputStream(targetFile, true).channel.use { targetChannel ->
  • files.forEach { file ->
  • FileInputStream(file).channel.use { sourceChannel ->
  • targetChannel.transferFrom(sourceChannel, targetChannel.size(), sourceChannel.size())
  • }
  • }
  • }
  • }
  • }
  • fun main() {
  • var start = System.currentTimeMillis()
  • val sourceFile = File("D:\\功夫.HD720高清国语中字版.mp4")
  • val splitCount = 3
  • val fileMd5 = FileUtil.getFileMd5(sourceFile)
  • println("计算原文件md5 = $fileMd5, 所花时间:${System.currentTimeMillis() - start}")
  • start = System.currentTimeMillis()
  • val files = FileUtil.splitFile(sourceFile, splitCount)
  • println("文件分割完成,所花时间:${System.currentTimeMillis() - start}")
  • start = System.currentTimeMillis()
  • val newFile = File("D:\\haha.mp4")
  • FileUtil.mergeFiles(files, newFile)
  • println("文件合并完成,所花时间:${System.currentTimeMillis() - start}")
  • start = System.currentTimeMillis()
  • val newFileMd5 = FileUtil.getFileMd5(newFile)
  • println("计算新文件md5 = $newFileMd5, 所花时间:${System.currentTimeMillis() - start}")
  • }

运行结果如下:

  • 计算原文件md5 = 870257cd37f47a81d528e5c871dc3901, 所花时间:3981
  • 文件分割完成,所花时间:615
  • 文件合并完成,所花时间:2610
  • 计算新文件md5 = 870257cd37f47a81d528e5c871dc3901, 所花时间:3711

分割与合并的文件如下:

在这里插入图片描述

功夫.HD720高清国语中字版.mp4是一个1.03G的视频文件,分割成了3个,然后又合并为1个。从打印的结果来看,计算md5是比较耗时的,比文件分割和合并都慢。而合并文件比分割文件要慢很多,这个是什么原理我也不是很清楚。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门