Android实现断点续传功能
本文实例为大家分享了android实现断点续传的具体代码,供大家参考,具体内容如下
断点续传功能,在文件上传中断时,下次上传同一文件时,能在上次的断点处继续上传,可节省时间和流量
总结规划步骤:
1.给大文件分片,每一个大文件发送前,相对应的创建一个文件夹存放所有分片
2.上传接口,每一个分片上传完成就删掉,直到所有分片上传完成,再删掉存放分片的文件夹,服务器把分片合成完整的文件。
先看分片功能,传入的3个参数分别为源文件地址,分片大小,存放分片的文件夹地址。返回的是分片个数。
/**
*
* @param sourcefilepath 源文件地址
* @param partfilelength 分割文件的每一个片段大小标准
* @param splitpath 分割之后片段所在文件夹
* @return
* @throws exception
*/
public static int splitfile(string sourcefilepath, int partfilelength, string splitpath) throws exception {
file sourcefile = null;
file targetfile = null;
inputstream ips = null;
outputstream ops = null;
outputstream configops = null;//该文件流用于存储文件分割后的相关信息,包括分割后的每个子文件的编号和路径,以及未分割前文件名
properties partinfo = null;//properties用于存储文件分割的信息
byte[] buffer = null;
int partnumber = 1;
sourcefile = new file(sourcefilepath);//待分割文件
ips = new fileinputstream(sourcefile);//找到读取源文件并获取输入流
//创建一个存放分片的文件夹
file tempfile = new file(splitpath);
if (!tempfile.exists()) {
tempfile.mkdirs();
}
configops = new fileoutputstream(new file(tempfile.getabsolutepath() + file.separator + "config.properties"));
buffer = new byte[partfilelength];//开辟缓存空间
int templength = 0;
partinfo = new properties();//key:1开始自动编号 value:文件路径
int slicecount = 0;
while ((templength = ips.read(buffer, 0, partfilelength)) != -1) {
string targetfilepath = tempfile.getabsolutepath() + file.separator + "part_" + (partnumber);//分割后的文件路径+文件名
slicecount = partnumber;
partinfo.setproperty((partnumber++) + "", targetfilepath);//将相关信息存储进properties
targetfile = new file(targetfilepath);
ops = new fileoutputstream(targetfile);//分割后文件
ops.write(buffer, 0, templength);//将信息写入碎片文件
ops.close();//关闭碎片文件
}
partinfo.setproperty("name", sourcefile.getname());//存储源文件名
partinfo.setproperty("slicecount", slicecount + "");//存储分片个数
partinfo.store(configops, "configfile");//将properties存储进实体文件中
ips.close();//关闭源文件流
return slicecount;
}接下来,和服务器协商接口,一共2个接口
1.uploadlargefilepre,传入参数除了常规上传文件参数之外加了分片个数slicecount和filehashcode。这一步是给服务器开辟存放分片的控件的,不执行上传操作,文件哈希值作为识别分片归属的唯一标准。返回int类型rcode,根据rcode结果判断是否执行第2步上传动作
2.uploadlargefile,传入参数除了常规上传文件参数之外加了分片path,分片index,isfinished。isfinished是最后一片的依据。客户端就在上传接口的response里继续上传下一个分片,直到最后一片上传完成,服务器才会返给这个大文件的url。
看代码
protected void addfilemsg(string path) {
forcelogin();
if (path == null) {
return;
}
file file = new file(path);
if (file.exists()) {
immsg msg = new immsg();
msg.settype(immsg.msg_type_file);
msg.settime(system.currenttimemillis());
msg.setmsgreaded();
msg.setreceivetime(system.currenttimemillis());
accountinfo accountinfo = imapp.instance().getaccountinfo();
msg.setuserid(accountinfo.getuserid());
msg.setsex(accountinfo.getsex());
msg.setusername(accountinfo.mnickname);
msg.setheadurl(accountinfo.mhead);
msg.mmasterid = mmasterid;
msg.setmsg(new file(path).getname());
msg.setpicpath(path);
msg.setstate(immsg.state_send_uploading);
string ext = null;
if (path.endswith("doc") || path.endswith("docx")) {
ext = "doc";
} else if (path.endswith("xls") || path.endswith("xlsx")) {
ext = "xls";
} else if (path.endswith("ppt") || path.endswith("pptx")) {
ext = "ppt";
} else if (path.endswith("pdf")) {
ext = "pdf";
}
msg.setmsg_extend3(ext);
msg.mfilesize = (int) new file(path).length();
msg.sethasatt(hasatt());
addmsgtodb(msg);
isfilelistsingle = filelist.size() == 1;
sphelper sphelper = sphelper.build();
int lastslices = sphelper.get(slice_old, 0);
string aespath = sphelper.get(slice_aesencpath, "");
string lastpath = sphelper.get(slice_picpath, "");
int temptotalcount = sphelper.get(commonutils.failed_temp_send_total_count, 0);//实际发送总个数
if (lastslices == 1 && temptotalcount > 1) {
/**
* 读取上次失败文件的进度,如果还剩一个失败文件,但是上次发送的总文件个数大于1,
* 则filelist.size() == 1这个判断不能用,在所有判断处加一个标志
*/
isfilelistsingle = false;
}
//根据文件大小,判断是否用分片上传 modify hexiaokang 2019年11月5日11点01分
if (new file(path).length() < global.file_spilt_length) {//文件小于5m,常规上传方式
uploadfile(msg);
} else {
file sourcefile = new file(path);
//分片所在文件夹,以未做aes加密的源文件名作为 分片文件夹名字
string slicedir = sourcefile.getparent() + file.separator + sourcefile.getname() + "_split";
if (lastslices == 1
&& path.equals(lastpath)
&& new file(slicedir).exists()
&& !aespath.equals("")
&& new file(aespath).exists()) {//发送上次的旧文件中断的分片
//只走一次
sphelper.put(slice_old, 0);
sliceindex = sphelper.get(slice_sliceindex, 0);
int count = sphelper.get(slice_slicecount, 0);
logutil2.i(tag + "&onuploadcomplete", "sendoldfiles sliceindex = " + sliceindex + ", slicecount = " + count);
largefilepre = true;
uploadlargefilepre(msg, count, slicedir, aespath);
} else {//发送大文件正常流程
//给文件分片
int partfilelength = global.file_spilt_length;//指定分割的子文件大小为5m
//先对文件做aes加密,再进行分片,这里很重要
string aesencpath = encryptmgr.encfile(path, accountinfo.getenctime(), accountinfo.getenckey());
file f = new file(aesencpath);
logutil2.i(tag + "&onuploadcomplete", "chatmsgactivity.addfilemsg: big file enc path=" + path);
try {
slicecount = filesliceutil.splitfile(aesencpath, partfilelength, slicedir);//将文件分割
} catch (exception e) {
logutil2.e(tag, "split.e:" + e.getmessage());
e.printstacktrace();
}
// logutil2.e(tag+"&onuploadcomplete", "chatmsgactivity.addfilemsg: slicecount:" + slicecount);
//分片上传
largefilepre = false;
uploadlargefilepre(msg, slicecount, slicedir, aesencpath);
}
}
}
}上面这一块代码,除了小文件常规上传,就是大文件上传方式了。大文件上传这里分了2种情况,
1.发送上次中断的大文件分片,这里就不用分片了,直接从sp拿到上次中断的分片count、index等信息直接上传
2.从分片到发送的全部流程,因为我这里对文件做了aes加密,所以是加密之后再分片
接下来就是上传过程
public void uploadlargefilepre(final immsg msg, final int slicecount, final string slicedir, final string aesencpath) {
if (msg.getstate() != immsg.state_send_waiting) {
msg.setstate(immsg.state_send_uploading);
mlistadapter.notifydatasetchanged();
}
accountinfo accountinfo = imapp.instance().getaccountinfo();
uploadfilehelper helper = new uploadfilehelper(accountinfo.getenctime(), accountinfo.getenckey(),
new uploadfilecallback() {
@override
public void onerror(call call, exception e) {
e.printstacktrace();
logutil2.e("onuploadcomplete", "chatmsgactivity.onerror: error(split_upload):" + e.tostring()+", call = "+call);
failcount++;
// if (msg.getpicpath() != null && msg.getpicpath().contains(sdcardutil.getsdcardpathex())) {
// new file(msg.getpicpath()).delete();
// }
btn_other_sendfile.setclickable(true);
commonutils.issendbtnclickable = true;
intent comintent = new intent();
comintent.setaction(commonutils.usbfile_complete_send);
comintent.putextra("fail_count", failcount);
sendbroadcast(comintent);
tempprogress = 0;
largefilepre = false;
//todo 上传失败,上传任务终止 (需要保存失败msg的信息,以及分片信息)
string picpath = msg.getpicpath();
// 保存picpath, sliceindex, slicecount, aesencpath
sphelper sphelper = sphelper.build();
sphelper.put(slice_picpath, picpath);
sphelper.put(slice_sliceindex, sliceindex);
sphelper.put(slice_slicecount, slicecount);
sphelper.put(slice_aesencpath, aesencpath);
sphelper.put(slice_old, 1);//标记,1表示有上次失败的碎片,处理完上次失败的碎片之后设置为0
return;
}
@override
public void onresponse(uploadfileackpacket uploadfileackpacket) {
logutil2.i("onuploadcomplete", "chatmsgactivity.onresponse: pre upload ack packet code="+uploadfileackpacket.getretcode()+", desc="+uploadfileackpacket.getretdesc());
if (getmsgfromdb(msg.getid()) == null) {
msg.setstate(immsg.state_send_failed);
logutil2.i("onuploadcomplete", "msg not exist d = (split_upload)" + msg.getid());
updatemsgtodb(msg);
mlistadapter.notifydatasetchanged();
failcount++;
btn_other_sendfile.setclickable(true);
commonutils.issendbtnclickable = true;
intent comintent = new intent();
comintent.setaction(commonutils.usbfile_complete_send);
sendbroadcast(comintent);
tempprogress = 0;
return;
}
if (uploadfileackpacket != null && uploadfileackpacket.issuccess()) {
logutil2.i("onuploadcomplete", "msg exist and sucess(uploadfileackpacket.sliceindex(split_upload)):" + uploadfileackpacket.sliceindex+", slicecount="+slicecount+", url="+uploadfileackpacket.murl);
if (sliceindex < slicecount && textutils.isempty(uploadfileackpacket.murl)) {
//更新进度条
if (isfilelistsingle) {//单个大文件
int pro = 100 * sliceindex / slicecount;
intent uploadintent = new intent();
uploadintent.setaction(commonutils.usbfile_progress_send);
uploadintent.putextra("sentcount", -1);//-1 代表发送的是单个文件
uploadintent.putextra("sentpro", pro);
sendbroadcast(uploadintent);
} else {//一次发送多个文件,包括大文件
}
//删除掉上传成功的分片
string slicepath = slicedir + file.separator + "part_" + (sliceindex);
new file(slicepath).delete();
sliceindex++;
//还有待上传的分片
uploadlargefilepre(msg, slicecount, slicedir, aesencpath);
} else {
//所有分片上传完成
largefilepre = false;
logutil2.i("onuploadcomplete", "chatmsgactivity.onresponse: 所有分片上传完成 largefilepre="+largefilepre);
msg.updateimageuploadprogress(100);
msg.setpicbigurl(uploadfileackpacket.murl);
//这里删除原文件
if (msg.getpicpath() != null && msg.getpicpath().contains(sdcardutil.getsdcardpathex())) {
file file = new file(msg.getpicpath());
//删除分片所在的文件夹
fileutil.deletefolderfile(slicedir, true);
//删除文件
file.delete();
// eventbus.getdefault().post(new encfileevent(encfileevent.com_file_delete, msg.getpicpath(), 0));
msg.setpicpath("");
}
if (msg.gettype() == immsg.msg_type_image) {
msg.setpicsmallurl(uploadfileackpacket.mthumbnail);
msg.mthumbwidth = this.mthumbwidth;
msg.mthumbheight = this.mthumbheight;
}
logutil2.i("onuploadcomplete", "msg exist and sucess(msg.getpicbigurl()(split_upload)):" + msg.getpicbigurl());
sendmsg(msg);
sentcount++;
sliceindex = 1;
if (isfilelistsingle) {//单个文件不用处理
} else {
//传递上传进度
intent uploadintent = new intent();
uploadintent.setaction(commonutils.usbfile_progress_send);
uploadintent.putextra("sentcount", sentcount);
sendbroadcast(uploadintent);
}
//继续上传下一个文件
if (sentcount < filelist.size()) {
addfilemsg(filelist.get(sentcount).getabsolutepath());
} else {
//所有文件上传完成
btn_other_sendfile.setclickable(true);
commonutils.issendbtnclickable = true;
intent comintent = new intent();
comintent.setaction(commonutils.usbfile_complete_send);
sendbroadcast(comintent);
tempprogress = 0;
}
}
} else {
logutil2.e("onuploadcomplete", "rcode(split_upload)):" + uploadfileackpacket.getretcode()+", sliceindex="+uploadfileackpacket.sliceindex);
if(uploadfileackpacket.getretcode()==1343688774){
logutil2.e("onuploadcomplete", "chatmsgactivity.onresponse: 缺片了!");
}
msg.setstate(immsg.state_send_failed);
updatemsgtodb(msg);
if (!immsgmgr.notifyactivity(immsgmgr.im_cmd_imp2pmsg_ack, msg.getuserid(), msg.hasatt(), false, msg)) {
}
mlistadapter.notifydatasetchanged();
}
}
@override
public void inprogress(float progress) {
super.inprogress(progress);
// logutil2.i("onuploadprogress", "inprogress " + progress+", path="+msg.getpicpath()+", slicedir="+slicedir);
logutil2.i("onuploadprogress", "chatmsgactivity.inprogress: slicecount="+slicecount+", sliceindex="+sliceindex+", sentcount="+sentcount+", list.size="+filelist.size()+", progress="+progress+", tempprogress="+tempprogress);
int pro = (int) (progress * 100);
if (new file(msg.getpicpath()).length() < global.file_spilt_length) {
//
}else{
int allpro= (int)(100*(progress+(sliceindex-1))/slicecount);
if (isfilelistsingle && allpro - tempprogress >= 1) {
//todo 分片上传,进度条重新计算,
//多个文件上传不用考虑,这里重新计算单个文件的上传问题
logutil2.i("onuploadprogress", "chatmsgactivity.inprogress: big file allpro="+allpro);
intent uploadintent = new intent();
uploadintent.setaction(commonutils.usbfile_progress_send);
uploadintent.putextra("sentcount", -1);//-1代表发送的是单个文件,这里是针对用户感知而言的代码逻辑
uploadintent.putextra("sentpro", allpro);
tempprogress = allpro;
sendbroadcast(uploadintent);
}
}
// 更新
mlistadapter.notifydatasetchanged();
}
}, msg);
logutil2.i("onuploadcomplete", "chatmsgactivity.uploadlargefilepre: largefilepre="+largefilepre);
if (largefilepre) {//todo 是否有过预处理
string slicepath = slicedir + file.separator + "part_" + (sliceindex);
int isfinished = sliceindex == slicecount ? 1 : 0;
helper.uploadlargefile(aesencpath, slicepath, sliceindex, isfinished);
} else {
//上传大文件 预处理
int rcode = helper.uploadlargefilepre(aesencpath, slicecount);
if (rcode == 0) {//预处理成功,可以执行上传任务
largefilepre = true;
string slicepath = slicedir + file.separator + "part_" + (sliceindex);
int isfinished = sliceindex == slicecount ? 1 : 0;
helper.uploadlargefile(aesencpath, slicepath, sliceindex, isfinished);
} else {
//预处理失败,不执行上传任务
logutil2.i("onuploadcomplete", "somewordsrcode:" + rcode+", before plus failecount="+failcount);
msg.setstate(immsg.state_send_failed);
updatemsgtodb(msg);
mlistadapter.notifydatasetchanged();
failcount++;
btn_other_sendfile.setclickable(true);
commonutils.issendbtnclickable = true;
intent comintent = new intent();
comintent.putextra("upload_error", 0);
comintent.putextra("fail_count",failcount);
comintent.setaction(commonutils.usbfile_complete_send);
logutil.logshow("onuploadcomplete", "chatmsgactivity.uploadlargefilepre: before sendintent failcount="+failcount);
sendbroadcast(comintent);
failcount=0;
tempprogress = 0;
}
}
}上面这一块就是上传过程
int rcode = helper.uploadlargefilepre(aesencpath, slicecount); 这一句为第一个接口预处理,根据结果决定是否执行上传。
helper.uploadlargefile(aesencpath, slicepath, sliceindex, isfinished); 这一句执行上传,在onresponse里处理上传结果,我这里有多个文件连续上传,所以结果处理看起来有点凌乱。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持萬仟网。
看完文章,还可以扫描下面的二维码下载快手极速版领4元红包
除了扫码领红包之外,大家还可以在快手极速版做签到,看视频,做任务,参与抽奖,邀请好友赚钱)。
邀请两个好友奖最高196元,如下图所示:







