A solution for downloading bugs from afinal breakpoints

Source: Internet
Author: User

As the first Android development framework in the country afinal, I believe that a lot of developers know. Although with the Android version of the iteration, some of these methods have a better solution but no one has ever doubted the value of afinal.

Recently in a breakpoint download function, referring to a number of examples, inadvertently found a bug in the Finalhttp.download () method.

First of all, we introduce the implementation principle of download Download in afinal. Unlike many other download methods, Afinal uses a single-threaded breakpoint download with no database or additional file operations. So how to implement the continuation of the breakpoint, mainly using a construction method of FileOutputStream , see the API documentation to see

The parameter append can continue to write data at the end of a file, so that the breakpoint continues to function.

Knowing the implementation principle, let's look at the code (the parameter name is slightly changed) you can see the complete code here

Public object handleentity (httpentity entity, entitycallback callback,             string target, boolean isresume)  throws IOException {        if  (Textutils.isempty (target)  | |  target.trim (). Length ()  == 0)              return null;        file targetfile = new  file (target);        if  (!targetfile.exists ())              targetfile.createnewfile ();         if  (mstop)  {             return targetFile;        }         long current = 0;         fileoutputstream os = null;        if  (IsResume)  {            current =  Targetfile.length ();             os = new  fileoutputstream (target, true);         } else {             os = new  FileOutputStream (target);        }         if  (mstop)  {             return targetfile;        }         inputstream input = entity.getconTent ();         long count = entity.getcontentlength ()  + current;        if  (Current >= count  | |  mstop)  {            return  targetfile;        }         int readlen = 0;        byte[] buffer =  new byte[1024];        while  (!mStop && ! ( Current >= count)                  &&  ((Readlen = input.read (buffer, 0, 1024))  >  0))  {//  not all read             os.write (buffer ,  0, reaDlen);            current += readlen;             callback.callback (Count, current,  false);        }         Callback.callback (count, current, true);        return  targetfile;    }

According to the code, we can see an obvious problem--the stream is not closed. The problem changed, I found the closure on the line I will not explain more. There is one more question, and that is the sentence

Long Count = entity.getcontentlength () + current;

The size of the remote file is constantly increasing, each time the download task is paused, the size of the remote file is increased once, and the increase is equal to the size of the locally downloaded fragmented file. There are two consequences of this: 1, this breakpoint download function is completely unused, each download starts from 0. 2, the local file as a continuation of the transmission, more and more large.

For example, the remote file size is 1024x768 B, the Local has read the size of 512b, while the user paused to download. The next time you re-download, the program re-read the remote file size, into the 1024+512 size, and the local file is 512,input.read again from 0 to read, and the local file starts from 512 to write, after the download is complete. Two downloads a total of 512 + 1024 bytes were downloaded, and the local file was downloaded for the first time by downloading the 512 fragmented file, and the second time the full 1024 file was downloaded, and the last download became a 1536-byte size file. (By the way, this file can be opened normally, but it will open more slowly than the source file)

Found the problem, the solution has, the simple will count = entity.getcontentlength () + current; change to count = Entity.getcontentlength (); And then the current<count inside the while loop is removed? You can try it, it's not going to work. First, this still cannot reach the purpose of the continuation of the breakpoint, as long as the continuation of the transmission

InputStream input = Entity.getcontent ();

This sentence gets the stream did not skip the already downloaded section, will not achieve the purpose of saving traffic to continue downloading, I think this should be understandable.

OK, then InputStream has a skip method, skip the downloaded part of the total can, no, also not, because the FileOutputStream is responsible for the continuation of the file written in a part of the data is corrupted, can not be used, And then the number of bytes you skip is not the number of bytes that really need to be skipped.

Go ahead, look at the code, here's My solution:

Public file handleentity (httpentity entity, downloadprogress callback,             file save, boolean isresume)   throws ioexception {        long current = 0;         RandomAccessFile file = new  Randomaccessfile (save,  "RW");        if  (IsResume)  {             current = file.length ();         }        inputstream  input = entity.getcontent ();         long count  = entity.getcontentlength ();        if  (mStop)  {              fileutils.closeio (file);             return save;        }                 current = input.skip (current );           file.seek (current);          int readLen = 0;         byte[] buffer = new byte[1024];        while  ((Readlen = input.read (buffer, 0, 1024))  != -1)  {             if  (mstop)  {                 break;            &nbSP;}  else {                 file.write (Buffer, 0, readlen);                 current += readLen;                 callback.onprogress (count, current);             }        }         callback.onprogress (count, current);         if  (Mstop && current < count)  { //   User Active Stop             fileutils.closeio (file);             throw new ioexception ("User  stop download thread ");        }         fileutils.closeio (file);        return save;     }

You can see the use of a randomaccessfile class that supports reading and writing to random access files to replace the FileOutputStream that can be resumed, while re-assigning values to the current

Current = Input.skip (current);

perfect resolves file corruption issues caused by unusable portions of fragmented files.

In fact, there is a small problem, in Android we all know that when downloading is usually accompanied by a progress bar display. This small problem is when the pause to continue downloading, because the period before the break of the damaged file is not available to take up the size of the download may be resumed when the progress bar rebound, which is very bad for the user experience, after all, who thought I worked hard to wait for a long time to read the bar, and suddenly back so much.

So the solution is actually to give a psychological comfort, see the following code

Public file handleentity (httpentity entity, downloadprogress callback,             file save, boolean isresume)   throws ioexception {        long current = 0;         RandomAccessFile file = new  Randomaccessfile (save,  "RW");        if  (IsResume)  {             current = file.length ();         }        inputstream  input = entity.getcontent ();         long count  = entity.getcontentlength ()  + current;        if   (mstop) &NBSP;{&NBSP;&NBSP;&NBSP;&NBSp;        fileutils.closeio (file);             return save;        }         //  here in fact this is wrong, the reason is so for the user experience, who do not want to download their own progress bar has gone more than half, Just because a pause suddenly a bunch of         /**          *  the actual wording here should be: <br>         * . Current = input.skip (current); <br>          * file.seek (current); <br>         *  Based on the explanation in the JDK documentation: the Inputstream.skip (long i) method skips I bytes and returns the number of bytes actually skipped. <br>         *  causes many of the reasons for this, skipping  n  Byte before the end of the file is just one possibility. Here I suspect that the damage caused by the fragment file is possible.          */&Nbsp;       file.seek (Input.skip (current));         int readLen = 0;        byte[]  buffer = new byte[1024];        while  ((ReadLen  = input.read (buffer, 0, 1024))  != -1)  {             if  (mstop)  {                 break;             } else {                 file.write (Buffer, 0, readlen);                 current += readLen;                  callback.onprogress (count, current);             }        }         callback.onprogress (count, current);         if  (mstop && current < count)  { //  user Active stop             fileutils.closeio (file);             throw new ioexception ("User stop  download thread ");        }         fileutils.closeio (file);        return save;     }


A solution for downloading bugs from afinal breakpoints

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.