Android平台HTTP断点续传的实现

           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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值