前言:之前有篇文章记录了一下Retrofit,这篇文章也是基于Retrofit实现的。接下来就记录一下,之前做的利用Retrofit实现的文件下载功能。
首先,定义接口与Retrofit对象
//下载文件 @Streaming @GET Call<ResponseBody> downloadFile(@Url String fileUrl);
public static POSTService getDownloadApi() {
postService = null;
OkHttpClient client = new OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
Retrofit build = new Retrofit.Builder()
.baseUrl(API_HOST)
.client(client)
.build();
postService = build.create(POSTService.class);
return postService;
}
然后,定义DownloadUtils类、FileUtils类以及DownloadListener接口
public class DownloadUtils {
private static final String TAG = "DownloadUtils";
// 视频下载相关
protected POSTService mApi;
private Handler mHandler;
private Call<ResponseBody> mCall;
private File mFile;
private Thread mThread;
// 下载到本地的文件目录
private String mFileFolder = Environment.getExternalStorageDirectory() + "/DownloadFile";
// 下载到本地的文件路径
private String mFilePath;
public DownloadUtils() {
if (mApi == null) {
mApi = RetrofitBean.getDownloadApi();
}
mHandler = new Handler();
}
public void downloadFile(String url, final DownloadListener downloadListener) {
//通过Url得到保存到本地的文件名
String name = url;
if (FileUtils.createOrExistsDir(mFileFolder)) {
int i = name.lastIndexOf('/');//一定是找最后一个'/'出现的位置
if (i != -1) {
name = name.substring(i);
mFilePath = mFileFolder + name;
}
}
if (TextUtils.isEmpty(mFilePath)) {
Log.e(TAG, "downloadVideo: 存储路径为空了");
return;
}
//建立一个文件
mFile = new File(mFilePath);
if (FileUtils.createOrExistsFile(mFile)) {
if (mApi == null) {
Log.e(TAG, "downloadVideo: 下载接口为空了");
return;
}
mCall = mApi.downloadFile(url);
mCall.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull final Response<ResponseBody> response) {
//下载文件放在子线程
mThread = new Thread() {
@Override
public void run() {
super.run();
//保存到本地
writeFile2Disk(response, mFile, downloadListener);
}
};
mThread.start();
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
downloadListener.onFailure("网络错误!");
}
});
}
}
private void writeFile2Disk(Response<ResponseBody> response, File file, final DownloadListener downloadListener) {
mHandler.post(new Runnable() {
@Override
public void run() {
downloadListener.onStart();
}
});
long currentLength = 0;
OutputStream os = null;
if (response.body() == null) {
mHandler.post(new Runnable() {
@Override
public void run() {
downloadListener.onFailure("资源错误!");
}
});
return;
}
InputStream is = response.body().byteStream();
final long totalLength = response.body().contentLength();
try {
os = new FileOutputStream(file);
int len;
byte[] buff = new byte[1024];
while ((len = is.read(buff)) != -1) {
os.write(buff, 0, len);
currentLength += len;
Log.e(TAG, "当前进度: " + currentLength);
final long finalCurrentLength = currentLength;
mHandler.post(new Runnable() {
@Override
public void run() {
downloadListener.onProgress((int) (100 * finalCurrentLength / totalLength));
if ((int) (100 * finalCurrentLength / totalLength) == 100) {
downloadListener.onFinish(mFilePath);
}
}
});
}
} catch (FileNotFoundException e) {
downloadListener.onFailure("未找到文件!");
e.printStackTrace();
} catch (IOException e) {
downloadListener.onFailure("IO错误!");
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class FileUtils {
/**
* 判断文件是否存在
*
* @param file 文件
* @return {@code true}: 存在<br>{@code false}: 不存在
*/
public static boolean isFileExists(final File file) {
return file != null && file.exists();
}
/**
* 判断文件是否存在,不存在则判断是否创建成功
*
* @param file 文件
* @return {@code true}: 存在或创建成功<br>{@code false}: 不存在或创建失败
*/
public static boolean createOrExistsFile(final File file) {
if (file == null) return false;
// 如果存在,是文件则返回 true,是目录则返回 false
if (file.exists()) return file.isFile();
if (!createOrExistsDir(file.getParentFile())) return false;
try {
return file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
/**
* 判断目录是否存在,不存在则判断是否创建成功
*
* @param file 文件
* @return {@code true}: 存在或创建成功<br>{@code false}: 不存在或创建失败
*/
public static boolean createOrExistsDir(final File file) {
// 如果存在,是目录则返回 true,是文件则返回 false,不存在则返回是否创建成功
return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
}
/**
* 判断目录是否存在,不存在则判断是否创建成功
*
* @param dirPath 目录路径
* @return {@code true}: 存在或创建成功<br>{@code false}: 不存在或创建失败
*/
public static boolean createOrExistsDir(final String dirPath) {
return createOrExistsDir(getFileByPath(dirPath));
}
/**
* 根据文件路径获取文件
*
* @param filePath 文件路径
* @return 文件
*/
public static File getFileByPath(final String filePath) {
return isSpace(filePath) ? null : new File(filePath);
}
private static boolean isSpace(final String s) {
if (s == null) return true;
for (int i = 0, len = s.length(); i < len; ++i) {
if (!Character.isWhitespace(s.charAt(i))) {
return false;
}
}
return true;
}
}
public interface DownloadListener {
void onStart();
void onProgress(int currentLength);
void onFinish(String localPath);
void onFailure(String errorInfo);
}
然后,开始下载
private void downloadFile(String url) {
DownloadUtils downloadUtils = new DownloadUtils();
downloadUtils.downloadFile(url, new DownloadListener() {
@Override
public void onStart() {
Log.e(TAG, "onStart: ");
isDowning = true;
Toast.makeText(MyApplication.getContextObject(),"正在下载,请稍后",Toast.LENGTH_SHORT).show();
}
@Override
public void onProgress(final int currentLength) {
Log.e(TAG, "onLoading: " + currentLength);
}
@Override
public void onFinish(String localPath) {
Log.e(TAG, "onFinish: " + localPath);
isDowning = false;
Toast.makeText(MyApplication.getContextObject(),"下载完成",Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(final String errorInfo) {
Log.e(TAG, "onFailure: " + errorInfo);
isDowning = false;
Toast.makeText(MyApplication.getContextObject(),"下载失败,请重试。。。",Toast.LENGTH_SHORT).show();
}
});
}
上面代码中定义了一个isDowning变量,它的作用是控制一次只会下载一个文件