SoFunction
Updated on 2025-04-06

Android implements breakpoint continuous transmission function

This article shares the specific code for Android to implement breakpoint continuation for your reference. The specific content is as follows

Breakpoint continuous transmission function, when file upload is interrupted, the next time the same file is uploaded, it can continue to upload at the last breakpoint, which can save time and traffic

Summary of planning steps:

1. Shard the large file. Before sending each large file, create a corresponding folder to store all the shards.

2. Upload the interface. Delete each shard after uploading it, until all shards are uploaded, then delete the folder where the shards are stored, and the server synthesizes the shards into a complete file.

Let’s look at the sharding function first. The three parameters passed in are the source file address, the shard size, and the folder address where the shard is stored. The number of shards is returned.

/**
      *
 * @param sourceFilePath   Source file address
 * @param partFileLength    Size standard for each fragment of the split file
 * @param splitPath         The folder where the fragment is located after splitting
 * @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;//This file stream is used to store relevant information after file segmentation, including the number and path of each subfile after segmentation, as well as the file name before segmentation        Properties partInfo = null;//properties are used to store file segmentation information        byte[] buffer = null;
        int partNumber = 1;
        sourceFile = new File(sourceFilePath);// File to be split        ips = new FileInputStream(sourceFile);//Finish the source file and get the input stream        //Create a folder to store shards        File tempFile = new File(splitPath);
        if (!()) {
            ();
        }
        configOps = new FileOutputStream(new File(() +  + ""));
        buffer = new byte[partFileLength];//Open the cache space        int tempLength = 0;
        partInfo = new Properties();//key:1 starts automatic numbering value: file path 
        int sliceCount = 0;
        while ((tempLength = (buffer, 0, partFileLength)) != -1) {
            String targetFilePath = () +  + "part_" + (partNumber);//Segmented file path + file name            sliceCount = partNumber;
            ((partNumber++) + "", targetFilePath);//Storage relevant information into properties            targetFile = new File(targetFilePath);
            ops = new FileOutputStream(targetFile);//Split file            (buffer, 0, tempLength);//Write information to fragmented file 
            ();//Close the fragmented file        }
        ("name", ());//Storage source file name        ("sliceCount", sliceCount + "");//Storage the number of shards        (configOps, "ConfigFile");//Storing properties into entity files        ();//Close the source file stream 
        return sliceCount;
    }

Next, negotiate the interface with the server, and there are 2 interfaces in total

, in addition to the regular upload file parameters, the number of shards is added sliceCount and fileHashcode. This step is to create controls for the server to store shards, and the upload operation is not performed. The file hash value is the only standard for identifying the shard's attributes. Return to the int type rCode, and determine whether to perform the second upload action based on the rCode result

, In addition to the regular upload file parameters, shard path, shard index, isFinished is added. isFinished is the basis for the last piece. The client continues to upload the next shard in the response of the upload interface. Until the last piece is uploaded, the server will return the URL of this large file.

Look at the code

protected void addFileMsg(String path) {
        forceLogin();
 
        if (path == null) {
            return;
        }
        File file = new File(path);
        if (()) {
            ImMsg msg = new ImMsg();
            (ImMsg.MSG_TYPE_FILE);
            (());
            ();
            (());
            AccountInfo accountInfo = ().getAccountInfo();
            (());
            (());
            ();
            ();
             = mMasterId;
            (new File(path).getName());
            (path);
            (ImMsg.STATE_SEND_UPLOADING);
            String ext = null;
            if (("doc") || ("docx")) {
                ext = "doc";
            } else if (("xls") || ("xlsx")) {
                ext = "xls";
            } else if (("ppt") || ("pptx")) {
                ext = "ppt";
            } else if (("pdf")) {
                ext = "pdf";
            }
            msg.setMsg_extend3(ext);
 
             = (int) new File(path).length();
            (hasAtt());
            addMsgToDB(msg);
 
            isFileListSingle = () == 1;
            SPHelper spHelper = ();
            int lastSlices = (slice_old, 0);
            String aesPath = (slice_aesEncPath, "");
            String lastPath = (slice_picPath, "");
            int tempTotalCount = (CommonUtils.FAILED_TEMP_SEND_TOTAL_COUNT, 0);//The total number of actual sent            if (lastSlices == 1 && tempTotalCount > 1) {
                /**
 * Read the progress of the last failed file. If there is still a failed file left, but the total number of files sent last time is greater than 1,
                  * Then () == 1 This judgment cannot be used, add a flag to all judgments
 */
                isFileListSingle = false;
            }
            //Judge whether to upload using shards based on file size modified hexiaokang November 5, 2019 at 11:01            if (new File(path).length() < Global.FILE_SPILT_LENGTH) {//The file is less than 5M, the regular upload method                upLoadFile(msg);
            } else {
 
                File sourceFile = new File(path);
                //The folder where the shard is located is the source file name that has not been encrypted in AES as the shard folder name                String sliceDir = () +  + () + "_split";
 
                if (lastSlices == 1
                        && (lastPath)
                        && new File(sliceDir).exists()
                        && !("")
                        && new File(aesPath).exists()) {//Send the fragmentation of the last old file interrupted                    //Only go once                    (slice_old, 0);
                    sliceIndex = (slice_sliceIndex, 0);
                    int count = (slice_sliceCount, 0);
                    (TAG + "&onUploadComplete", "sendOldFiles sliceIndex = " + sliceIndex + ", sliceCount = " + count);
                    largeFilePre = true;
                    upLoadLargeFilePre(msg, count, sliceDir, aesPath);
                } else {//The normal process of sending large files                    //Slice the file                    int partFileLength = Global.FILE_SPILT_LENGTH;//Specify that the divided subfile size is 5M                    //AES encryption is first performed on the file, and then sharding it. It is very important here.                    String aesEncPath = (path, (), ());
                    File f = new File(aesEncPath);
                    (TAG + "&onUploadComplete", ": big file enc path=" + path);
 
                    try {
                        sliceCount = (aesEncPath, partFileLength, sliceDir);//Splitting the file                    } catch (Exception e) {
                        (TAG, ":" + ());
                        ();
                    }
//                  (TAG+"&onUploadComplete", ": sliceCount:" + sliceCount);
 
                    //Shash upload                    largeFilePre = false;
                    upLoadLargeFilePre(msg, sliceCount, sliceDir, aesEncPath);
 
                }
            }
        }
    }

In addition to the regular upload of small files, the above code is the way to upload large files. There are two situations for uploading large files here.

1. Send the last interrupted large file fragments, and there is no need to shard them here. You can directly get the last interrupted fragment count, index and other information from the sp.

2. The entire process from sharding to sending, because I have AES encryption on the file, so I am encrypting it and then sharding it.

Next is the upload process

public void upLoadLargeFilePre(final ImMsg msg, final int sliceCount, final String sliceDir, final String aesEncPath) {
        if (() != ImMsg.STATE_SEND_WAITING) {
            (ImMsg.STATE_SEND_UPLOADING);
            ();
        }
        AccountInfo accountInfo = ().getAccountInfo();
        UploadFileHelper helper = new UploadFileHelper((), (),
                new UploadFileCallback() {
 
                    @Override
                    public void onError(Call call, Exception e) {
                        ();
                        ("onUploadComplete", ": error(split_upload):" + ()+", call = "+call);
                        failCount++;
//                            if (() != null && ().contains(())) {
//                                new File(()).delete();
//                            }
                        btn_other_sendfile.setClickable(true);
                         = true;
                        Intent comIntent = new Intent();
                        (CommonUtils.USBFILE_COMPLETE_SEND);
                        ("fail_count", failCount);
                        sendBroadcast(comIntent);
 
                        tempProgress = 0;
                        largeFilePre = false;
                        //Todo upload failed, upload task terminates (requires saved msg information and shard information)                        String picPath = ();
                        // Save picPath, sliceIndex, sliceCount, aesEncPath                        SPHelper spHelper = ();
                        (slice_picPath, picPath);
                        (slice_sliceIndex, sliceIndex);
                        (slice_sliceCount, sliceCount);
                        (slice_aesEncPath, aesEncPath);
                        (slice_old, 1);// Tag, 1 means that there was a fragment that failed last time. After processing the fragment that failed last time, it is set to 0.                        return;
                    }
 
                    @Override
                    public void onResponse(UploadFileAckPacket uploadFileAckPacket) {
                        ("onUploadComplete", ": pre upload ack packet code="+()+", desc="+());
                        if (getMsgFromDB(()) == null) {
                            (ImMsg.STATE_SEND_FAILED);
                            ("onUploadComplete", "msg not exist d = (split_upload)" + ());
                            updateMsgToDB(msg);
                            ();
 
                            failCount++;
                            btn_other_sendfile.setClickable(true);
                             = true;
                            Intent comIntent = new Intent();
                            (CommonUtils.USBFILE_COMPLETE_SEND);
                            sendBroadcast(comIntent);
 
                            tempProgress = 0;
                            return;
                        }
                        if (uploadFileAckPacket != null && ()) {
                            ("onUploadComplete", "msg exist and sucess((split_upload)):" + +", sliceCount="+sliceCount+", url="+);
                            if (sliceIndex < sliceCount && ()) {
                                //Update progress bar                                if (isFileListSingle) {//Single large file                                    int pro = 100 * sliceIndex / sliceCount;
 
                                    Intent uploadIntent = new Intent();
                                    (CommonUtils.USBFILE_PROGRESS_SEND);
                                    ("sentCount", -1);//-1 means that a single file is sent                                    ("sentPro", pro);
                                    sendBroadcast(uploadIntent);
                                } else {//Send multiple files at once, including large files 
                                }
                                //Delete the successfully uploaded fragment                                String slicePath = sliceDir +  + "part_" + (sliceIndex);
                                new File(slicePath).delete();
                                sliceIndex++;
                                //There are still fragments to be uploaded                                upLoadLargeFilePre(msg, sliceCount, sliceDir, aesEncPath);
                            } else {
                                //All shards are uploaded                                largeFilePre = false;
                                ("onUploadComplete", ": All shards are uploaded largeFilePre="+largeFilePre);
                                (100);
                                ();
 
                                //Delete the original file here 
                                if (() != null && ().contains(())) {
                                    File file = new File(());
                                    //Delete the folder where the shard is located                                    (sliceDir, true);
                                    //Delete the file                                    ();
//                                    ().post(new EncFileEvent(EncFileEvent.COM_FILE_DELETE, (), 0));
                                    ("");
                                }
                                if (() == ImMsg.MSG_TYPE_IMAGE) {
                                    ();
                                     = ;
                                     = ;
                                }
 
 
                                ("onUploadComplete", "msg exist and sucess(()(split_upload)):" + ());
                                sendMsg(msg);
                                sentCount++;
                                sliceIndex = 1;
 
                                if (isFileListSingle) {//Single file does not need to be processed 
                                } else {
                                    //Pass upload progress                                    Intent uploadIntent = new Intent();
                                    (CommonUtils.USBFILE_PROGRESS_SEND);
                                    ("sentCount", sentCount);
                                    sendBroadcast(uploadIntent);
                                }
 
                                //Continue to upload the next file                                if (sentCount < ()) {
                                    addFileMsg((sentCount).getAbsolutePath());
                                } else {
                                    //All files are uploaded                                    btn_other_sendfile.setClickable(true);
                                     = true;
                                    Intent comIntent = new Intent();
                                    (CommonUtils.USBFILE_COMPLETE_SEND);
                                    sendBroadcast(comIntent);
 
                                    tempProgress = 0;
                                }
                            }
                        } else {
                            ("onUploadComplete", "rCode(split_upload)):" + ()+", sliceIndex="+);
                            if(()==1343688774){
                                ("onUploadComplete", ": The film is missing!");
                            }
                            (ImMsg.STATE_SEND_FAILED);
                            updateMsgToDB(msg);
                            if (!(IMMsgMgr.IM_CMD_IMP2PMSG_ACK, (), (), false, msg)) {
                                
                            }
                            ();
                        }
                    }
 
                    @Override
                    public void inProgress(float progress) {
                        (progress);
//                        ("onUploadProgress", "inProgress " + progress+", path="+()+", sliceDir="+sliceDir);
                        ("onUploadProgress", ": sliceCount="+sliceCount+", sliceIndex="+sliceIndex+", sentCount="+sentCount+", ="+()+", progress="+progress+", tempProgress="+tempProgress);
                        int pro = (int) (progress * 100);
                        if (new File(()).length() < Global.FILE_SPILT_LENGTH) {
//
                        }else{
                            int allPro= (int)(100*(progress+(sliceIndex-1))/sliceCount);
                            if (isFileListSingle && allPro - tempProgress >= 1) {
                                //todo shard upload, progress bar recalculate,                                //No need to consider uploading multiple files, here is the uploading problem of a single file recalculation here                                
                                ("onUploadProgress", ": big file allPro="+allPro);
                                Intent uploadIntent = new Intent();
                                (CommonUtils.USBFILE_PROGRESS_SEND);
                                ("sentCount", -1);//-1 means that the sending is a single file, here is the code logic for user perception                                ("sentPro", allPro);
                                tempProgress = allPro;
                                sendBroadcast(uploadIntent);
                            }
                        }
                        // renew                        ();
                    }
                }, msg);
        ("onUploadComplete", ": largeFilePre="+largeFilePre);
        if (largeFilePre) {//Todo has any preprocessing            String slicePath = sliceDir +  + "part_" + (sliceIndex);
            int isFinished = sliceIndex == sliceCount ? 1 : 0;
            (aesEncPath, slicePath, sliceIndex, isFinished);
        } else {
            //Upload large files and preprocess            int rCode = (aesEncPath, sliceCount);
            if (rCode == 0) {//The preprocessing is successful, the upload task can be executed                largeFilePre = true;
                String slicePath = sliceDir +  + "part_" + (sliceIndex);
                int isFinished = sliceIndex == sliceCount ? 1 : 0;
                (aesEncPath, slicePath, sliceIndex, isFinished);
            } else {
                //Preprocessing failed, upload task is not executed                ("onUploadComplete", "somewordsrCode:" + rCode+", before plus faileCount="+failCount);
 
                (ImMsg.STATE_SEND_FAILED);
                updateMsgToDB(msg);
                ();
                failCount++;
                btn_other_sendfile.setClickable(true);
                 = true;
                Intent comIntent = new Intent();
                ("upload_error", 0);
                ("fail_count",failCount);
                (CommonUtils.USBFILE_COMPLETE_SEND);
                ("onUploadComplete", ": before sendIntent failCount="+failCount);
                sendBroadcast(comIntent);
 
                failCount=0;
                tempProgress = 0;
            }
        }
    }

The above is the upload process

int rCode = (aesEncPath, sliceCount); This sentence is preprocessed by the first interface, and determines whether to perform uploading based on the results.

(aesEncPath, slicePath, sliceIndex, isFinished); This sentence performs uploading and processes the upload results in onResponse. I have multiple files uploaded continuously, so the result processing looks a bit messy.

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.