<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本文範例為大家分享了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裡處理上傳結果,我這裡有多個檔案連續上傳,所以結果處理看起來有點凌亂。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45