Android平台HTTP断点续传的实现

来源:互联网 发布:墨脱公路沥青路面数据 编辑:程序博客网 时间:2024/05/22 10:11

           HTTP对断点续传提供了良好的支持,具体来说,要实现单线程断点续传,需要考虑以下问题:

       (1)我们需要如何去下载文件,因为很多时候因为网络差、程序被kill掉等原因,文件无法一次下载完,我们需要继续发起下载文件的HTTP请求,这个时候不需要从头开始下,只需要告诉服务器,我们要从哪里开始下,哪里结束,可以通过设置“Range”字段的属性来实现。例如:

httpUrl.setRequestProperty("Range", "bytes=" + mCurrentDownloadLength + "-" + mFileTotalSize);

       (2)需要保存文件的下载进度,这个我最开始尝试着使用SharedPreferences保存进度,经过测试后发现使用该方法保存的进度不一定准确,可能的原因是在保持的时候程序中断了,比较可靠的方式是获取文件的大小,用.length()方法。

        (3)如何从指定的位置开始写文件,使用RandomAccessFile即可。

       (4)由于在弱网环境下,下载的文件可能会出错,所以对下载的文件需要进行MD5值校验。

       (5)此外,我们下载时,也需要知道文件的总大小,这样不用每次启动下载时,都进行MD5值校验是否下载完成,这个可以通过HTTP的请求实现。

具体的实现代码不复杂,部分代码如下所示:

public class DownloadTask extends Thread {        private URL mDownloadUrl;    private Activity mActivity;        private String mZipFilePathStr;    // 保存用户设置的so目录    public static String mUserBaiduNaviSDK_SO = null;    // 需要下载的文件的总大小    private int mFileTotalSize;    private int mCurrentDownloadLength = 0;        private DownloadCallBack mCallback;    // 每次读取的文件大小    private int mBlockSize = 4 * 1024;        private SharedPreferences mSharedPref;        private int timeout = 50 * 1000; // 连接超时时间        public DownloadTask(Activity activity, String sdcardRootPath, String appFolderName) {        this.mActivity = activity;    }        public DownloadTask(Activity activity, String sdcardRootPath, String appFolderName, URL downloadUrl,            DownloadCallBack callback) {        this.mActivity = activity;        this.mDownloadUrl = downloadUrl;        this.mCallback = callback;    }        @Override    public void run() {        mSharedPref = mActivity.getPreferences(Context.MODE_PRIVATE);        // String str = sdcardRootPath + File.separator + appFolderName + File.separator + "1.txt";        init();                if (mCurrentDownloadLength < mFileTotalSize) {            try {                HttpURLConnection httpUrl = (HttpURLConnection) mDownloadUrl.openConnection();                if (httpUrl == null) {                    return ;                }                setRequest(httpUrl);                downloadFile(httpUrl);                mCallback.downloadWorkOver();            } catch (Exception e) {                return ;            }        }    }        private void init() {        mFileTotalSize = getTotalFileSize();        mCurrentDownloadLength = getCurrentDownloadLength();    }        /*     *      */    private int getCurrentDownloadLength() {        File file = new File(ZipUtils.getTmpZipFile());        mCurrentDownloadLength = file.length();        LogUtil.e("dingbbin", "dingbbin getCurrentDownloadLength is " + mCurrentDownloadLength);        return mCurrentDownloadLength;    }        /*     * 获取需要更新的文件的大小     */    private int getTotalFileSize() {    //    return 8038626;        HttpURLConnection urlConnection = null;        int length = 0;        try {            urlConnection = (HttpURLConnection) mDownloadUrl.openConnection();            urlConnection.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg,"                    + " application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument,"                            + " application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel,"                                + " application/vnd.ms-powerpoint, application/msword, */*");            urlConnection.setRequestProperty("Referer", mDownloadUrl.toString());            urlConnection.setRequestProperty("Connection", "Keep-Alive");            urlConnection.setRequestProperty("Accept-Encoding", "identity");            int status = urlConnection.getResponseCode();            if (status == 200) {                length = urlConnection.getContentLength();            }        } catch (IOException e) {            e.printStackTrace();        } finally {            if (urlConnection != null) {                urlConnection.disconnect();            }        }               return length;    }        private void setRequest(HttpURLConnection httpUrl) throws ProtocolException {        httpUrl.setConnectTimeout(timeout);        httpUrl.setRequestMethod("GET"); // 设置请求方法        httpUrl.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg,"                + " application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument,"                        + " application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel,"                            + " application/vnd.ms-powerpoint, application/msword, */*");        httpUrl.setRequestProperty("Referer", mDownloadUrl.toString());        httpUrl.setRequestProperty("Charset", "UTF-8");        httpUrl.setRequestProperty("Accept-Encoding", "identity");                // 设置开始和结束范围        // int endPos = fileTotalSize;        httpUrl.setRequestProperty("Range", "bytes=" + mCurrentDownloadLength + "-" + mFileTotalSize);                httpUrl.setRequestProperty("Connection", "Keep-Alive");    }        private void downloadFile(HttpURLConnection httpUrl) {        InputStream inStream = null;        RandomAccessFile accessFile = null;        try {            inStream = httpUrl.getInputStream();            byte[] buffer = new byte[mBlockSize];            int offset = 0;            accessFile = new RandomAccessFile(ZipUtils.getTmpZipFile(), "rwd");            // 定位到pos位置            accessFile.seek(mCurrentDownloadLength);            while ((offset = inStream.read(buffer, 0, mBlockSize)) != -1) {                accessFile.write(buffer, 0, offset);                // 更新已经下载的大小                mCurrentDownloadLength += offset;                LogUtil.e("dingbbin", "dingbbin current length is " + mCurrentDownloadLength);            }            if (mCurrentDownloadLength >= mFileTotalSize) {                // download finish, rename file                ZipUtils.getTmpZipFile().renameTo(ZipUtils.getZipFile());                resetLength();            }        } catch (IOException e) {            e.printStackTrace();        } finally {            if (null != inStream) {                try {                    inStream.close();                } catch (IOException e) {                    LogUtil.e("", e.toString());                }            }            if (null != accessFile) {                try {                    accessFile.close();                } catch (IOException e) {                    LogUtil.e("", e.toString());                }            }            if (httpUrl != null) {                httpUrl.disconnect();            }        }    }        private void resetLength() {        Editor editor = mSharedPref.edit();        editor.putInt(DynamicLoadConstants.NAVI_DOWNLOAD_LENGTH, 0);        editor.commit();    }        private void unzipFile() {        try {            ZipUtils.decompress(ZipUtils.getZipFile(), new File(ZipUtils.getDownloadSoDir()));        } catch (Exception e) {            e.printStackTrace();        }                // 删除下载的文件        ZipUtils.getZipFile().delete();    }        public interface DownloadCallBack {        public void downloadWorkOver();    }}
需要注意的地方有2个,就是我们在获取文件总大小和下载文件时,发下Java和Android的实现不太一样,具体来说,同样的代码,同样的API,在Java环境中可以正常获取大小,可以实现断点续传,但是在Android上就不行,需要设置urlConnection.setRequestProperty("Accept-Encoding", "identity")才可以,具体的原因我也是不甚明白。

0 0
原创粉丝点击