欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android实现大文件断点上传

程序员文章站 2022-06-02 14:42:00
...

前言

 之前项目需要上传大文件的功能,上传大文件经常遇到上传一半由于网络或者其他一些原因上传失败。然后又得重新上传(很麻烦),所以就想能不能做个断点上传的功能。于是网上搜索,发现市面上很少有断点上传的案例,有找到一个案例也是采用SOCKET作为上传方式(大文件上传,不适合使用POST,GET形式)。由于大文件夹不适合http上传的方式,所以就想能不能把大文件切割成n块小文件,然后上传这些小文件,所有小文件全部上传成功后再在服务器上进行拼接。这样不就可以实现断点上传,又解决了http不适合上传大文件的难题了吗!!!

原理分析

*******Android客户端********

首先,android端调用服务器接口1,参数为filename(服务器标识判断是否上传过)

如果存在filename,说明之前上传过,则续传;如果没有,则从零开始上传。

然后,android端调用服务器接口2,传入参数name,chunck(传到第几块),chuncks(总共多少块)


android实现大文件断点上传android实现大文件断点上传

*******服务器端********

接口一:根据上传文件名称filename 判断是否之前上传过,没有则返回客户端chunck=1,有则读取记录chunck并返回

接口二:上传文件,如果上传块数chunck=chuncks,遍历所有块文件拼接成一个完整文件。


接口1

  1. @WebServlet(urlPatterns = { "/ckeckFileServlet" })
  2. public class CkeckFileServlet extends HttpServlet {
  3. private FileUploadStatusServiceI statusService;
  4. String repositoryPath;
  5. String uploadPath;
  6. @Override
  7. public void init(ServletConfig config) throws ServletException {
  8. ServletContext servletContext = config.getServletContext();
  9. WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
  10. statusService = (FileUploadStatusServiceI) context.getBean("fileUploadStatusServiceImpl");
  11. repositoryPath = FileUtils.getTempDirectoryPath();
  12. uploadPath = config.getServletContext().getRealPath("datas/uploader");
  13. File up = new File(uploadPath);
  14. if (!up.exists()) {
  15. up.mkdir();
  16. }
  17. }
  18. @Override
  19. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  20. // TODO Auto-generated method stub
  21. String fileName = new String(req.getParameter("filename"));
  22. //String chunk = req.getParameter("chunk");
  23. //System.out.println(chunk);
  24. System.out.println(fileName);
  25. resp.setContentType("text/json; charset=utf-8");
  26. TfileUploadStatus file = statusService.get(fileName);
  27. try {
  28. if (file != null) {
  29. int schunk = file.getChunk();
  30. deleteFile(uploadPath + schunk + "_" + fileName);
  31. //long off = schunk * Long.parseLong(chunkSize);
  32. resp.getWriter().write("{\"off\":" + schunk + "}");
  33. } else {
  34. resp.getWriter().write("{\"off\":1}");
  35. }
  36. } catch (Exception e) {
  37. // TODO Auto-generated catch block
  38. e.printStackTrace();
  39. }
  40. }
  41. }

接口2

  1. @WebServlet(urlPatterns = { "/uploaderWithContinuinglyTransferring" })
  2. public class UploaderServletWithContinuinglyTransferring extends HttpServlet {
  3. private static final long serialVersionUID = 1L;
  4. private FileUploadStatusServiceI statusService;
  5. String repositoryPath;
  6. String uploadPath;
  7. @Override
  8. public void init(ServletConfig config) throws ServletException {
  9. ServletContext servletContext = config.getServletContext();
  10. WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
  11. statusService = (FileUploadStatusServiceI) context.getBean("fileUploadStatusServiceImpl");
  12. repositoryPath = FileUtils.getTempDirectoryPath();
  13. System.out.println("临时目录:" + repositoryPath);
  14. uploadPath = config.getServletContext().getRealPath("datas/uploader");
  15. System.out.println("目录:" + uploadPath);
  16. File up = new File(uploadPath);
  17. if (!up.exists()) {
  18. up.mkdir();
  19. }
  20. }
  21. @SuppressWarnings("unchecked")
  22. public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  23. response.setCharacterEncoding("UTF-8");
  24. Integer schunk = null;// 分割块数
  25. Integer schunks = null;// 总分割数
  26. String name = null;// 文件名
  27. BufferedOutputStream outputStream = null;
  28. if (ServletFileUpload.isMultipartContent(request)) {
  29. try {
  30. DiskFileItemFactory factory = new DiskFileItemFactory();
  31. factory.setSizeThreshold(1024);
  32. factory.setRepository(new File(repositoryPath));// 设置临时目录
  33. ServletFileUpload upload = new ServletFileUpload(factory);
  34. upload.setHeaderEncoding("UTF-8");
  35. upload.setSizeMax(5 * 1024 * 1024 * 1024);// 设置附近大小
  36. List<FileItem> items = upload.parseRequest(request);
  37. // 生成新文件名
  38. String newFileName = null;
  39. for (FileItem item : items) {
  40. if (!item.isFormField()) {// 如果是文件类型
  41. name = newFileName;// 获得文件名
  42. if (name != null) {
  43. String nFname = newFileName;
  44. if (schunk != null) {
  45. nFname = schunk + "_" + name;
  46. }
  47. File savedFile = new File(uploadPath, nFname);
  48. item.write(savedFile);
  49. }
  50. } else {
  51. // 判断是否带分割信息
  52. if (item.getFieldName().equals("chunk")) {
  53. schunk = Integer.parseInt(item.getString());
  54. //System.out.println(schunk);
  55. }
  56. if (item.getFieldName().equals("chunks")) {
  57. schunks = Integer.parseInt(item.getString());
  58. }
  59. if (item.getFieldName().equals("name")) {
  60. newFileName = new String(item.getString());
  61. }
  62. }
  63. }
  64. //System.out.println(schunk + "/" + schunks);
  65. if (schunk != null && schunk == 1) {
  66. TfileUploadStatus file = statusService.get(newFileName);
  67. if (file != null) {
  68. statusService.updateChunk(newFileName, schunk);
  69. } else {
  70. statusService.add(newFileName, schunk, schunks);
  71. }
  72. } else {
  73. TfileUploadStatus file = statusService.get(newFileName);
  74. if (file != null) {
  75. statusService.updateChunk(newFileName, schunk);
  76. }
  77. }
  78. if (schunk != null && schunk.intValue() == schunks.intValue()) {
  79. outputStream = new BufferedOutputStream(new FileOutputStream(new File(uploadPath, newFileName)));
  80. // 遍历文件合并
  81. for (int i = 1; i <= schunks; i++) {
  82. //System.out.println("文件合并:" + i + "/" + schunks);
  83. File tempFile = new File(uploadPath, i + "_" + name);
  84. byte[] bytes = FileUtils.readFileToByteArray(tempFile);
  85. outputStream.write(bytes);
  86. outputStream.flush();
  87. tempFile.delete();
  88. }
  89. outputStream.flush();
  90. }
  91. response.getWriter().write("{\"status\":true,\"newName\":\"" + newFileName + "\"}");
  92. } catch (FileUploadException e) {
  93. e.printStackTrace();
  94. response.getWriter().write("{\"status\":false}");
  95. } catch (Exception e) {
  96. e.printStackTrace();
  97. response.getWriter().write("{\"status\":false}");
  98. } finally {
  99. try {
  100. if (outputStream != null)
  101. outputStream.close();
  102. } catch (IOException e) {
  103. e.printStackTrace();
  104. }
  105. }
  106. }
  107. }
  108. }


android端
UploadTask 上传线程类
  1. package com.mainaer.wjoklib.okhttp.upload;
  2. import android.database.sqlite.SQLiteDatabase;
  3. import android.os.Environment;
  4. import android.os.Handler;
  5. import android.os.Looper;
  6. import android.os.Message;
  7. import android.text.TextUtils;
  8. import java.io.Closeable;
  9. import java.io.File;
  10. import java.io.IOException;
  11. import java.text.DecimalFormat;
  12. import java.util.HashMap;
  13. import java.util.Map;
  14. import okhttp3.Headers;
  15. import okhttp3.MediaType;
  16. import okhttp3.MultipartBody;
  17. import okhttp3.OkHttpClient;
  18. import okhttp3.Request;
  19. import okhttp3.RequestBody;
  20. import okhttp3.Response;
  21. /**
  22. * 上传线程
  23. *
  24. * @author hst
  25. * @date 2016/9/6 .
  26. */
  27. public class UploadTask implements Runnable {
  28. private static String FILE_MODE = "rwd";
  29. private OkHttpClient mClient;
  30. private SQLiteDatabase db;
  31. private UploadTaskListener mListener;
  32. private Builder mBuilder;
  33. private String id;// task id
  34. private String url;// file url
  35. private String fileName; // File name when saving
  36. private int uploadStatus;
  37. private int chunck, chuncks;//流块
  38. private int position;
  39. private int errorCode;
  40. static String BOUNDARY = "----------" + System.currentTimeMillis();
  41. public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("multipart/form-data;boundary=" + BOUNDARY);
  42. private UploadTask(Builder builder) {
  43. mBuilder = builder;
  44. mClient = new OkHttpClient();
  45. this.id = mBuilder.id;
  46. this.url = mBuilder.url;
  47. this.fileName = mBuilder.fileName;
  48. this.uploadStatus = mBuilder.uploadStatus;
  49. this.chunck = mBuilder.chunck;
  50. this.setmListener(mBuilder.listener);
  51. // 以kb为计算单位
  52. }
  53. @Override
  54. public void run() {
  55. try {
  56. int blockLength = 1024 * 1024;
  57. File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator +fileName);
  58. if (file.length() % blockLength == 0) {
  59. chuncks = (int) file.length() / blockLength;
  60. } else {
  61. chuncks = (int) file.length() / blockLength + 1;
  62. }
  63. while (chunck <= chuncks&&uploadStatus!= UploadStatus.UPLOAD_STATUS_PAUSE&&uploadStatus!= UploadStatus.UPLOAD_STATUS_ERROR)
  64. {
  65. uploadStatus = UploadStatus.UPLOAD_STATUS_UPLOADING;
  66. Map<String, String> params = new HashMap<String, String>();
  67. params.put("name", fileName);
  68. params.put("chunks", chuncks + "");
  69. params.put("chunk", chunck + "");
  70. final byte[] mBlock = FileUtils.getBlock((chunck - 1) * blockLength, file, blockLength);
  71. MultipartBody.Builder builder = new MultipartBody.Builder()
  72. .setType(MultipartBody.FORM);
  73. addParams(builder, params);
  74. RequestBody requestBody = RequestBody.create(MEDIA_TYPE_MARKDOWN, mBlock);
  75. builder.addFormDataPart("mFile", fileName, requestBody);
  76. Request request = new Request.Builder()
  77. .url(url+ "uploaderWithContinuinglyTransferring")
  78. .post(builder.build())
  79. .build();
  80. Response response = null;
  81. response = mClient.newCall(request).execute();
  82. if (response.isSuccessful()) {
  83. onCallBack();
  84. chunck++;
  85. /* if (chunck <= chuncks) {
  86. run();
  87. }*/
  88. }
  89. else
  90. {
  91. uploadStatus = UploadStatus.UPLOAD_STATUS_ERROR;
  92. onCallBack();
  93. }
  94. }
  95. } catch (IOException e) {
  96. uploadStatus = UploadStatus.UPLOAD_STATUS_ERROR;
  97. onCallBack();
  98. e.printStackTrace();
  99. }
  100. }
  101. /* *//**
  102. * 删除数据库文件和已经上传的文件
  103. *//*
  104. public void cancel() {
  105. if (mListener != null)
  106. mListener.onCancel(UploadTask.this);
  107. }*/
  108. /**
  109. * 分发回调事件到ui层
  110. */
  111. private void onCallBack() {
  112. mHandler.sendEmptyMessage(uploadStatus);
  113. // 同步manager中的task信息
  114. //UploadManager.getInstance().updateUploadTask(this);
  115. }
  116. Handler mHandler = new Handler(Looper.getMainLooper()) {
  117. @Override
  118. public void handleMessage(Message msg) {
  119. int code = msg.what;
  120. switch (code) {
  121. // 上传失败
  122. case UploadStatus.UPLOAD_STATUS_ERROR:
  123. mListener.onError(UploadTask.this, errorCode,position);
  124. break;
  125. // 正在上传
  126. case UploadStatus.UPLOAD_STATUS_UPLOADING:
  127. mListener.onUploading(UploadTask.this, getDownLoadPercent(), position);
  128. // 暂停上传
  129. break;
  130. case UploadStatus.UPLOAD_STATUS_PAUSE:
  131. mListener.onPause(UploadTask.this);
  132. break;
  133. }
  134. }
  135. };
  136. private String getDownLoadPercent() {
  137. String baifenbi = "0";// 接受百分比的值
  138. if (chunck >= chuncks) {
  139. return "100";
  140. }
  141. double baiy = chunck * 1.0;
  142. double baiz = chuncks * 1.0;
  143. // 防止分母为0出现NoN
  144. if (baiz > 0) {
  145. double fen = (baiy / baiz) * 100;
  146. //NumberFormat nf = NumberFormat.getPercentInstance();
  147. //nf.setMinimumFractionDigits(2); //保留到小数点后几位
  148. // 百分比格式,后面不足2位的用0补齐
  149. //baifenbi = nf.format(fen);
  150. //注释掉的也是一种方法
  151. DecimalFormat df1 = new DecimalFormat("0");//0.00
  152. baifenbi = df1.format(fen);
  153. }
  154. return baifenbi;
  155. }
  156. private String getFileNameFromUrl(String url) {
  157. if (!TextUtils.isEmpty(url)) {
  158. return url.substring(url.lastIndexOf("/") + 1);
  159. }
  160. return System.currentTimeMillis() + "";
  161. }
  162. private void close(Closeable closeable) {
  163. try {
  164. closeable.close();
  165. } catch (IOException e) {
  166. e.printStackTrace();
  167. }
  168. }
  169. public void setClient(OkHttpClient mClient) {
  170. this.mClient = mClient;
  171. }
  172. public Builder getBuilder() {
  173. return mBuilder;
  174. }
  175. public void setBuilder(Builder builder) {
  176. this.mBuilder = builder;
  177. }
  178. public String getId() {
  179. if (!TextUtils.isEmpty(id)) {
  180. } else {
  181. id = url;
  182. }
  183. return id;
  184. }
  185. public String getUrl() {
  186. return url;
  187. }
  188. public String getFileName() {
  189. return fileName;
  190. }
  191. public void setUploadStatus(int uploadStatus) {
  192. this.uploadStatus = uploadStatus;
  193. }
  194. public int getUploadStatus() {
  195. return uploadStatus;
  196. }
  197. public void setmListener(UploadTaskListener mListener) {
  198. this.mListener = mListener;
  199. }
  200. public static class Builder {
  201. private String id;// task id
  202. private String url;// file url
  203. private String fileName; // File name when saving
  204. private int uploadStatus = UploadStatus.UPLOAD_STATUS_INIT;
  205. private int chunck;//第几块
  206. private UploadTaskListener listener;
  207. /**
  208. * 作为上传task开始、删除、停止的key值,如果为空则默认是url
  209. *
  210. * @param id
  211. * @return
  212. */
  213. public Builder setId(String id) {
  214. this.id = id;
  215. return this;
  216. }
  217. /**
  218. * 上传url(not null)
  219. *
  220. * @param url
  221. * @return
  222. */
  223. public Builder setUrl(String url) {
  224. this.url = url;
  225. return this;
  226. }
  227. /**
  228. * 设置上传状态
  229. *
  230. * @param uploadStatus
  231. * @return
  232. */
  233. public Builder setUploadStatus(int uploadStatus) {
  234. this.uploadStatus = uploadStatus;
  235. return this;
  236. }
  237. /**
  238. * 第几块
  239. *
  240. * @param chunck
  241. * @return
  242. */
  243. public Builder setChunck(int chunck) {
  244. this.chunck = chunck;
  245. return this;
  246. }
  247. /**
  248. * 设置文件名
  249. *
  250. * @param fileName
  251. * @return
  252. */
  253. public Builder setFileName(String fileName) {
  254. this.fileName = fileName;
  255. return this;
  256. }
  257. /**
  258. * 设置上传回调
  259. *
  260. * @param listener
  261. * @return
  262. */
  263. public Builder setListener(UploadTaskListener listener) {
  264. this.listener = listener;
  265. return this;
  266. }
  267. public UploadTask build() {
  268. return new UploadTask(this);
  269. }
  270. }
  271. private void addParams(MultipartBody.Builder builder, Map<String, String> params) {
  272. if (params != null && !params.isEmpty()) {
  273. for (String key : params.keySet()) {
  274. builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + key + "\""),
  275. RequestBody.create(null, params.get(key)));
  276. }
  277. }
  278. }
  279. }

UploadManager上传管理类
  1. package com.mainaer.wjoklib.okhttp.upload;
  2. import android.content.Context;
  3. import android.database.sqlite.SQLiteDatabase;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6. import java.util.concurrent.ExecutorService;
  7. import java.util.concurrent.Executors;
  8. import java.util.concurrent.Future;
  9. import java.util.concurrent.TimeUnit;
  10. import okhttp3.OkHttpClient;
  11. /**
  12. * 上传管理器
  13. *
  14. * @author wangjian
  15. * @date 2016/5/13 .
  16. */
  17. public class UploadManager {
  18. private static Context mContext;
  19. private static SQLiteDatabase db;
  20. private OkHttpClient mClient;
  21. private int mPoolSize = 20;
  22. // 将执行结果保存在future变量中
  23. private Map<String, Future> mFutureMap;
  24. private ExecutorService mExecutor;
  25. private Map<String, UploadTask> mCurrentTaskList;
  26. static UploadManager manager;
  27. /**
  28. * 方法加锁,防止多线程操作时出现多个实例
  29. */
  30. private static synchronized void init() {
  31. if (manager == null) {
  32. manager = new UploadManager();
  33. }
  34. }
  35. /**
  36. * 获得当前对象实例
  37. *
  38. * @return 当前实例对象
  39. */
  40. public final static UploadManager getInstance() {
  41. if (manager == null) {
  42. init();
  43. }
  44. return manager;
  45. }
  46. /**
  47. * 管理器初始化,建议在application中调用
  48. *
  49. * @param context
  50. */
  51. public static void init(Context context, SQLiteDatabase db1) {
  52. mContext = context;
  53. db = db1;
  54. getInstance();
  55. }
  56. public UploadManager() {
  57. initOkhttpClient();
  58. // 初始化线程池
  59. mExecutor = Executors.newFixedThreadPool(mPoolSize);
  60. mFutureMap = new HashMap<>();
  61. mCurrentTaskList = new HashMap<>();
  62. }
  63. /**
  64. * 初始化okhttp
  65. */
  66. private void initOkhttpClient() {
  67. OkHttpClient.Builder okBuilder = new OkHttpClient.Builder();
  68. okBuilder.connectTimeout(1000, TimeUnit.SECONDS);
  69. okBuilder.readTimeout(1000, TimeUnit.SECONDS);
  70. okBuilder.writeTimeout(1000, TimeUnit.SECONDS);
  71. mClient = okBuilder.build();
  72. }
  73. /**
  74. * 添加上传任务
  75. *
  76. * @param uploadTask
  77. */
  78. public void addUploadTask(UploadTask uploadTask) {
  79. if (uploadTask != null && !isUploading(uploadTask)) {
  80. uploadTask.setClient(mClient);
  81. uploadTask.setUploadStatus(UploadStatus.UPLOAD_STATUS_INIT);
  82. // 保存上传task列表
  83. mCurrentTaskList.put(uploadTask.getId(), uploadTask);
  84. Future future = mExecutor.submit(uploadTask);
  85. mFutureMap.put(uploadTask.getId(), future);
  86. }
  87. }
  88. private boolean isUploading(UploadTask task) {
  89. if (task != null) {
  90. if (task.getUploadStatus() == UploadStatus.UPLOAD_STATUS_UPLOADING) {
  91. return true;
  92. }
  93. }
  94. return false;
  95. }
  96. /**
  97. * 暂停上传任务
  98. *
  99. * @param id 任务id
  100. */
  101. public void pause(String id) {
  102. UploadTask task = getUploadTask(id);
  103. if (task != null) {
  104. task.setUploadStatus(UploadStatus.UPLOAD_STATUS_PAUSE);
  105. }
  106. }
  107. /**
  108. * 重新开始已经暂停的上传任务
  109. *
  110. * @param id 任务id
  111. */
  112. public void resume(String id, UploadTaskListener listener) {
  113. UploadTask task = getUploadTask(id);
  114. if (task != null) {
  115. addUploadTask(task);
  116. }
  117. }
  118. /* *//**
  119. * 取消上传任务(同时会删除已经上传的文件,和清空数据库缓存)
  120. *
  121. * @param id 任务id
  122. * @param listener
  123. *//*
  124. public void cancel(String id, UploadTaskListener listener) {
  125. UploadTask task = getUploadTask(id);
  126. if (task != null) {
  127. mCurrentTaskList.remove(id);
  128. mFutureMap.remove(id);
  129. task.setmListener(listener);
  130. task.cancel();
  131. task.setDownloadStatus(UploadStatus.DOWNLOAD_STATUS_CANCEL);
  132. }
  133. }*/
  134. /**
  135. * 实时更新manager中的task信息
  136. *
  137. * @param task
  138. */
  139. public void updateUploadTask(UploadTask task) {
  140. if (task != null) {
  141. UploadTask currTask = getUploadTask(task.getId());
  142. if (currTask != null) {
  143. mCurrentTaskList.put(task.getId(), task);
  144. }
  145. }
  146. }
  147. /**
  148. * 获得指定的task
  149. *
  150. * @param id task id
  151. * @return
  152. */
  153. public UploadTask getUploadTask(String id) {
  154. UploadTask currTask = mCurrentTaskList.get(id);
  155. if (currTask == null) {
  156. currTask = parseEntity2Task(new UploadTask.Builder().build());
  157. // 放入task list中
  158. mCurrentTaskList.put(id, currTask);
  159. }
  160. return currTask;
  161. }
  162. private UploadTask parseEntity2Task(UploadTask currTask) {
  163. UploadTask.Builder builder = new UploadTask.Builder()//
  164. .setUploadStatus(currTask.getUploadStatus())
  165. .setFileName(currTask.getFileName())//
  166. .setUrl(currTask.getUrl())
  167. .setId(currTask.getId());
  168. currTask.setBuilder(builder);
  169. return currTask;
  170. }
  171. }
FileUtils文件分块类
  1. package com.mainaer.wjoklib.okhttp.upload;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.io.RandomAccessFile;
  5. public class FileUtils {
  6. public static byte[] getBlock(long offset, File file, int blockSize) {
  7. byte[] result = new byte[blockSize];
  8. RandomAccessFile accessFile = null;
  9. try {
  10. accessFile = new RandomAccessFile(file, "r");
  11. accessFile.seek(offset);
  12. int readSize = accessFile.read(result);
  13. if (readSize == -1) {
  14. return null;
  15. } else if (readSize == blockSize) {
  16. return result;
  17. } else {
  18. byte[] tmpByte = new byte[readSize];
  19. System.arraycopy(result, 0, tmpByte, 0, readSize);
  20. return tmpByte;
  21. }
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. } finally {
  25. if (accessFile != null) {
  26. try {
  27. accessFile.close();
  28. } catch (IOException e1) {
  29. }
  30. }
  31. }
  32. return null;
  33. }
  34. }
UploadTaskListener接口类管理类
  1. package com.mainaer.wjoklib.okhttp.upload;
  2. import com.mainaer.wjoklib.okhttp.download.DownloadStatus;
  3. import java.io.File;
  4. /**
  5. * Created by hst on 16/9/21.
  6. */
  7. public interface UploadTaskListener {
  8. /**
  9. * 上传中
  10. *
  11. * @param percent
  12. * @param uploadTask
  13. */
  14. void onUploading(UploadTask uploadTask, String percent,int position)
  15. /**
  16. * 上传成功
  17. *
  18. * @param file
  19. * @param uploadTask
  20. */
  21. void onUploadSuccess(UploadTask uploadTask, File file);
  22. /**
  23. * 上传失败
  24. *
  25. * @param uploadTask
  26. * @param errorCode {@link DownloadStatus}
  27. */
  28. void onError(UploadTask uploadTask, int errorCode,int position);
  29. /**
  30. * 上传暂停
  31. *
  32. * @param uploadTask
  33. *
  34. */
  35. void onPause(UploadTask uploadTask);
  36. }

android源码地址:https://github.com/handsometong/okhttpUpLoader
相关标签: 上传下载