(点击
上方公众号
,可快速关注)
来源:伯乐在线专栏作者 - Code4Android
链接:http://android.jobbole.com/85154/
点击 → 了解如何加入专栏作者
前言
现在越来越多的应用开始有上传大文件的需求,以及秒传,续传功能。由于最近学习大文件分隔上传,以及秒传的实现,给予分享的这种精神,我想将自己的学习过程,以及遇到的问题做一个总结,希望对有这方面需求的小伙伴有一定的帮助。
源码传送门
分析
说到大文件上传,我们可能首先会想的一些网盘App,这些优秀的网盘除了上传大文件外,还可以实现秒传以及断点续传功能。说起断点续传也就明白了文章题目所说的大文件分片,由于网络的原因,一个大文件例如一百兆的文件,很有可能中途上传到一半或者50MB,或者上传到99MB时候失败了,如果下次再上传时还从头开始上传,这个体验很少人能接受的,如果你要真做成这样的话,那么客户一定会严重流失,所以我们需要对其分片或者说下次上传时从失败的地方开始上传。相信使用网盘较多的朋友都知道有一个很6的功能就是秒传,可能你很难相信为何我几百兆甚至几个G的文件,为何几秒内就上传成功了,为何这么神奇,其实原理也很简单,就是我们每次上传文件时每一个文件都会有一个独一无二的特征值,当我们上传文件时,他首先会检测服务器是否有该特征值的文件,,如果有的话,就不需要占用网络带宽,直接复制一份到你的网盘。今天分享的这篇文章便是为秒传打下坚实基础的,获取大文件的特征值-MD5.
MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由罗纳德·李维斯特设计,于1992年公开,用以替换MD4算法
MessageDigest
在java.security这个包下有一个类MessageDigest ,通过名字我们就知道是消息摘要的意思,那么本篇文章也是有MessageDigest 这个类展开讨论。
//方法1:返回MessageDigest实例 algorithm算法名称
public
static
MessageDigest getInstance
(
String
algorithm
)
throws
NoSuchAlgorithmException
{}
//方法2:更新计算消息摘要的数据内容
public
void
update
(
byte
[]
input
)
{}
//方法3:计算消息摘要并重置
public
byte
[]
digest
(){}
对于计算文件的MD5,我们主要用的上面的几个方法。方法1主要是进行初始化操作,需要指定算法,方法2是进行消息摘要内容的更新。而方法3就是最重要的一步,计算消息摘要的值并返回。
读取文件
对于文件的读取有很多种方式,例如通过FileInputStream读取字节流,也可以包装成InputStreamReader读取字节流,也可以包装成BufferedInputStream进行带缓冲区的读取,以及RandomAccessFile或者nio 包中FileChannel加内存映射的方式。当然各种方式的性能不言而喻(对流不熟悉的自行补脑)。
具体实现
FileInputStream字节流方式
/**
* 获取文件的MD5值
*
* @param file 文件路径
* @return md5
*/
public
static
String
getFileMd5
(
File
file
)
{
MessageDigest
messageDigest
;
//MappedByteBuffer byteBuffer = null;
FileInputStream
fis
=
null
;
try
{
messageDigest
=
MessageDigest
.
getInstance
(
"MD5"
);
if
(
file
==
null
)
{
return
""
;
}
if
(
!
file
.
exists
())
{
return
""
;
}
int
len
=
0
;
fis
=
new
FileInputStream
(
file
);
//普通流读取方式
byte
[]
buffer
=
new
byte
[
1024
*
1024
*
10
];
while
((
len
=
fis
.
read
(
buffer
))
>
0
)
{
//该对象通过使用 update()方法处理数据
messageDigest
.
update
(
buffer
,
0
,
len
);
}
BigInteger
bigInt
=
new
BigInteger
(
1
,
messageDigest
.
digest
());
String
md5
=
bigInt
.
toString
(
16
);
while
(
md5
.
length
()
32
)
{
md5
=
"0"
+
md5
;
}
return
md5
;
}
catch
(
NoSuchAlgorithmException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
FileNotFoundException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
IOException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
finally
{
try
{
if
(
fis
!=
null
)
{
fis
.
close
();
fis
=
null
;
}
}
catch
(
IOException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
}
return
""
;
}
FileChannel +MappedByteBuffer 方式
/**
* FileChannel 获取文件的MD5值
*
* @param file 文件路径
* @return md5
*/
public
static
String
getFileMd52
(
File
file
)
{
MessageDigest
messageDigest
;
FileInputStream
fis
=
null
;
FileChannel
ch
=
null
;
try
{
messageDigest
=
MessageDigest
.
getInstance
(
"MD5"
);
if
(
file
==
null
)
{
return
""
;
}
if
(
!
file
.
exists
())
{
return
""
;
}
fis
=
new
FileInputStream
(
file
);
ch
=
fis
.
getChannel
();
int
size
=
1024
*
1024
*
10
;
long
part
=
file
.
length
()
/
size
+
(
file
.
length
()
%
size
>
0
?
1
:
0
);
System
.
err
.
println
(
"文件分片数"
+
part
);
for
(
int
j
=
0
;
j
part
;
j
++
)
{
MappedByteBuffer
byteBuffer
=
ch
.
map
(
FileChannel
.
MapMode
.
READ_ONLY
,
j
*
size
,
j
==
part
-
1
?
file
.
length
()
:
(
j
+
1
)
*
size
);
messageDigest
.
update
(
byteBuffer
);
byteBuffer
.
clear
();
}
BigInteger
bigInt
=
new
BigInteger
(
1
,
messageDigest
.
digest
());
String
md5
=
bigInt
.
toString
(
16
);
while
(
md5
.
length
()
32
)
{
md5
=
"0"
+
md5
;
}
return
md5
;
}
catch
(
NoSuchAlgorithmException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
FileNotFoundException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
IOException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
finally
{
try
{
if
(
fis
!=
null
)
{
fis
.
close
();
fis
=
null
;
}
if
(
ch
!=
null
){
ch
.
close
();
ch
=
null
;
}
}
catch
(
IOException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
}
return
""
;
}
/**
* FileChannel 获取文件的MD5值
*
* @param file 文件路径
* @return md5
*/
public
static
String
getFileMd52
(
File
file
)
{
MessageDigest
messageDigest
;
FileInputStream
fis
=
null
;
FileChannel
ch
=
null
;
try
{
messageDigest
=
MessageDigest
.
getInstance
(
"MD5"
);
if
(
file
==
null
)
{
return
""
;
}
if
(
!
file
.
exists
())
{
return
""
;
}
fis
=
new
FileInputStream
(
file
);
ch
=
fis
.
getChannel
();
int
size
=
1024
*
1024
*
10
;
long
part
=
file
.
length
()
/
size
+
(
file
.
length