前言:之前有篇文章记录了一下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变量,它的作用是控制一次只会下载一个文件