您的位置:首页 > 理论基础 > 计算机网络

Android平台HTTP断点续传的实现

2015-08-29 22:48 555 查看
           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 {

4000
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")才可以,具体的原因我也是不甚明白。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android java http