本文实例为大家分享了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加密 , 所以是加密之后再分片
接下来就是上传过程
【Android实现断点续传功能】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里处理上传结果 , 我这里有多个文件连续上传 , 所以结果处理看起来有点凌乱 。
以上就是本文的全部内容 , 希望对大家的学习有所帮助 , 也希望大家多多支持www.887551.com 。
-- 展开阅读全文 --
推荐阅读
- 蜂蛹怎么吃最有营养,蜂子幼崽怎么吃营养最佳
- Android实现多线程断点续传
- Android实现多张图片合成加载动画
- 古诗春风的意思
- wifi6跟wifi5的区别
- 重庆为什么简称渝 重庆市简称渝的由来
- 3月这4大星座旧爱来袭,再续前缘,重燃爱火
- 这些星座在一起时间越长,彼此越深爱,感情只增不减
- 古代胭脂是指什么