首页 > 系统 > Android > 正文

android中okhttp实现断点上传示例

2019-12-12 03:44:05
字体:
来源:转载
供稿:网友

前言

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

原理分析

Android客户端

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

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

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

 

服务器端

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

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

 服务端源代码

服务器接口1

@WebServlet(urlPatterns = { "/ckeckFileServlet" })public class CkeckFileServlet extends HttpServlet {  private FileUploadStatusServiceI statusService;  String repositoryPath;  String uploadPath;  @Override  public void init(ServletConfig config) throws ServletException {    ServletContext servletContext = config.getServletContext();    WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);    statusService = (FileUploadStatusServiceI) context.getBean("fileUploadStatusServiceImpl");    repositoryPath = FileUtils.getTempDirectoryPath();    uploadPath = config.getServletContext().getRealPath("datas/uploader");    File up = new File(uploadPath);    if (!up.exists()) {      up.mkdir();    }  }  @Override  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {    // TODO Auto-generated method stub    String fileName = new String(req.getParameter("filename"));    //String chunk = req.getParameter("chunk");    //System.out.println(chunk);    System.out.println(fileName);    resp.setContentType("text/json; charset=utf-8");    TfileUploadStatus file = statusService.get(fileName);    try {      if (file != null) {        int schunk = file.getChunk();        deleteFile(uploadPath + schunk + "_" + fileName);        //long off = schunk * Long.parseLong(chunkSize);        resp.getWriter().write("{/"off/":" + schunk + "}");      } else {        resp.getWriter().write("{/"off/":1}");      }    } catch (Exception e) {      // TODO Auto-generated catch block      e.printStackTrace();    }  }}

服务器接口2

@WebServlet(urlPatterns = { "/uploaderWithContinuinglyTransferring" })public class UploaderServletWithContinuinglyTransferring extends HttpServlet {  private static final long serialVersionUID = 1L;  private FileUploadStatusServiceI statusService;  String repositoryPath;  String uploadPath;  @Override  public void init(ServletConfig config) throws ServletException {    ServletContext servletContext = config.getServletContext();    WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);    statusService = (FileUploadStatusServiceI) context.getBean("fileUploadStatusServiceImpl");    repositoryPath = FileUtils.getTempDirectoryPath();    System.out.println("临时目录:" + repositoryPath);    uploadPath = config.getServletContext().getRealPath("datas/uploader");    System.out.println("目录:" + uploadPath);    File up = new File(uploadPath);    if (!up.exists()) {      up.mkdir();    }  }  @SuppressWarnings("unchecked")  public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {    response.setCharacterEncoding("UTF-8");    Integer schunk = null;// 分割块数    Integer schunks = null;// 总分割数    String name = null;// 文件名    BufferedOutputStream outputStream = null;    if (ServletFileUpload.isMultipartContent(request)) {      try {        DiskFileItemFactory factory = new DiskFileItemFactory();        factory.setSizeThreshold(1024);        factory.setRepository(new File(repositoryPath));// 设置临时目录        ServletFileUpload upload = new ServletFileUpload(factory);        upload.setHeaderEncoding("UTF-8");        upload.setSizeMax(5 * 1024 * 1024 * 1024);// 设置附近大小        List<FileItem> items = upload.parseRequest(request);        // 生成新文件名        String newFileName = null;         for (FileItem item : items) {          if (!item.isFormField()) {// 如果是文件类型            name = newFileName;// 获得文件名            if (name != null) {              String nFname = newFileName;              if (schunk != null) {                nFname = schunk + "_" + name;              }              File savedFile = new File(uploadPath, nFname);              item.write(savedFile);            }          } else {            // 判断是否带分割信息            if (item.getFieldName().equals("chunk")) {              schunk = Integer.parseInt(item.getString());              //System.out.println(schunk);            }            if (item.getFieldName().equals("chunks")) {              schunks = Integer.parseInt(item.getString());            }            if (item.getFieldName().equals("name")) {              newFileName = new String(item.getString());            }          }        }        //System.out.println(schunk + "/" + schunks);        if (schunk != null && schunk == 1) {          TfileUploadStatus file = statusService.get(newFileName);          if (file != null) {            statusService.updateChunk(newFileName, schunk);          } else {            statusService.add(newFileName, schunk, schunks);          }        } else {          TfileUploadStatus file = statusService.get(newFileName);          if (file != null) {            statusService.updateChunk(newFileName, schunk);          }        }        if (schunk != null && schunk.intValue() == schunks.intValue()) {          outputStream = new BufferedOutputStream(new FileOutputStream(new File(uploadPath, newFileName)));          // 遍历文件合并          for (int i = 1; i <= schunks; i++) {            //System.out.println("文件合并:" + i + "/" + schunks);            File tempFile = new File(uploadPath, i + "_" + name);            byte[] bytes = FileUtils.readFileToByteArray(tempFile);            outputStream.write(bytes);            outputStream.flush();            tempFile.delete();          }          outputStream.flush();        }        response.getWriter().write("{/"status/":true,/"newName/":/"" + newFileName + "/"}");      } catch (FileUploadException e) {        e.printStackTrace();        response.getWriter().write("{/"status/":false}");      } catch (Exception e) {        e.printStackTrace();        response.getWriter().write("{/"status/":false}");      } finally {        try {          if (outputStream != null)            outputStream.close();        } catch (IOException e) {          e.printStackTrace();        }      }    }  }}

android端源码

UploadTask 上传线程类

package com.mainaer.wjoklib.okhttp.upload;import android.database.sqlite.SQLiteDatabase;import android.os.Environment;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.text.TextUtils;import java.io.Closeable;import java.io.File;import java.io.IOException;import java.text.DecimalFormat;import java.util.HashMap;import java.util.Map;import okhttp3.Headers;import okhttp3.MediaType;import okhttp3.MultipartBody;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.RequestBody;import okhttp3.Response;/** * 上传线程 * * @author hst * @date 2016/9/6 . */  public class UploadTask implements Runnable {  private static String FILE_MODE = "rwd";  private OkHttpClient mClient;  private SQLiteDatabase db;  private UploadTaskListener mListener;  private Builder mBuilder;  private String id;// task id  private String url;// file url  private String fileName; // File name when saving  private int uploadStatus;  private int chunck, chuncks;//流块  private int position;  private int errorCode;  static String BOUNDARY = "----------" + System.currentTimeMillis();  public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("multipart/form-data;boundary=" + BOUNDARY);  private UploadTask(Builder builder) {    mBuilder = builder;    mClient = new OkHttpClient();    this.id = mBuilder.id;    this.url = mBuilder.url;    this.fileName = mBuilder.fileName;    this.uploadStatus = mBuilder.uploadStatus;    this.chunck = mBuilder.chunck;    this.setmListener(mBuilder.listener);    // 以kb为计算单位  }  @Override  public void run() {    try {      int blockLength = 1024 * 1024;      File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator +fileName);      if (file.length() % blockLength == 0) {        chuncks = (int) file.length() / blockLength;      } else {        chuncks = (int) file.length() / blockLength + 1;      }      while (chunck <= chuncks&&uploadStatus!= UploadStatus.UPLOAD_STATUS_PAUSE&&uploadStatus!= UploadStatus.UPLOAD_STATUS_ERROR)      {        uploadStatus = UploadStatus.UPLOAD_STATUS_UPLOADING;        Map<String, String> params = new HashMap<String, String>();        params.put("name", fileName);        params.put("chunks", chuncks + "");        params.put("chunk", chunck + "");        final byte[] mBlock = FileUtils.getBlock((chunck - 1) * blockLength, file, blockLength);        MultipartBody.Builder builder = new MultipartBody.Builder()            .setType(MultipartBody.FORM);        addParams(builder, params);        RequestBody requestBody = RequestBody.create(MEDIA_TYPE_MARKDOWN, mBlock);        builder.addFormDataPart("mFile", fileName, requestBody);        Request request = new Request.Builder()            .url(url+ "uploaderWithContinuinglyTransferring")            .post(builder.build())            .build();        Response response = null;        response = mClient.newCall(request).execute();        if (response.isSuccessful()) {          onCallBack();          chunck++;          /* if (chunck <= chuncks) {             run();          }*/        }        else        {          uploadStatus = UploadStatus.UPLOAD_STATUS_ERROR;          onCallBack();        }      }    } catch (IOException e) {      uploadStatus = UploadStatus.UPLOAD_STATUS_ERROR;      onCallBack();      e.printStackTrace();    }  }/*  *//**   * 删除数据库文件和已经上传的文件   *//*  public void cancel() {    if (mListener != null)      mListener.onCancel(UploadTask.this);  }*/  /**   * 分发回调事件到ui层   */  private void onCallBack() {    mHandler.sendEmptyMessage(uploadStatus);    // 同步manager中的task信息    //UploadManager.getInstance().updateUploadTask(this);  }  Handler mHandler = new Handler(Looper.getMainLooper()) {    @Override    public void handleMessage(Message msg) {      int code = msg.what;      switch (code) {        // 上传失败        case UploadStatus.UPLOAD_STATUS_ERROR:          mListener.onError(UploadTask.this, errorCode,position);          break;        // 正在上传        case UploadStatus.UPLOAD_STATUS_UPLOADING:          mListener.onUploading(UploadTask.this, getDownLoadPercent(), position);         // 暂停上传          break;        case UploadStatus.UPLOAD_STATUS_PAUSE:          mListener.onPause(UploadTask.this);          break;      }    }  };  private String getDownLoadPercent() {    String baifenbi = "0";// 接受百分比的值    if (chunck >= chuncks) {      return "100";    }    double baiy = chunck * 1.0;    double baiz = chuncks * 1.0;    // 防止分母为0出现NoN    if (baiz > 0) {      double fen = (baiy / baiz) * 100;      //NumberFormat nf = NumberFormat.getPercentInstance();      //nf.setMinimumFractionDigits(2); //保留到小数点后几位      // 百分比格式,后面不足2位的用0补齐      //baifenbi = nf.format(fen);      //注释掉的也是一种方法      DecimalFormat df1 = new DecimalFormat("0");//0.00      baifenbi = df1.format(fen);    }    return baifenbi;  }  private String getFileNameFromUrl(String url) {    if (!TextUtils.isEmpty(url)) {      return url.substring(url.lastIndexOf("/") + 1);    }    return System.currentTimeMillis() + "";  }  private void close(Closeable closeable) {    try {      closeable.close();    } catch (IOException e) {      e.printStackTrace();    }  }  public void setClient(OkHttpClient mClient) {    this.mClient = mClient;  }  public Builder getBuilder() {    return mBuilder;  }  public void setBuilder(Builder builder) {    this.mBuilder = builder;  }  public String getId() {    if (!TextUtils.isEmpty(id)) {    } else {      id = url;    }    return id;  }  public String getUrl() {    return url;  }  public String getFileName() {    return fileName;  }  public void setUploadStatus(int uploadStatus) {    this.uploadStatus = uploadStatus;  }  public int getUploadStatus() {    return uploadStatus;  }  public void setmListener(UploadTaskListener mListener) {    this.mListener = mListener;  }  public static class Builder {    private String id;// task id    private String url;// file url    private String fileName; // File name when saving    private int uploadStatus = UploadStatus.UPLOAD_STATUS_INIT;    private int chunck;//第几块    private UploadTaskListener listener;    /**     * 作为上传task开始、删除、停止的key值,如果为空则默认是url     *     * @param id     * @return     */    public Builder setId(String id) {      this.id = id;      return this;    }    /**     * 上传url(not null)     *     * @param url     * @return     */    public Builder setUrl(String url) {      this.url = url;      return this;    }    /**     * 设置上传状态     *     * @param uploadStatus     * @return     */    public Builder setUploadStatus(int uploadStatus) {      this.uploadStatus = uploadStatus;      return this;    }    /**     * 第几块     *     * @param chunck     * @return     */    public Builder setChunck(int chunck) {      this.chunck = chunck;      return this;    }    /**     * 设置文件名     *     * @param fileName     * @return     */    public Builder setFileName(String fileName) {      this.fileName = fileName;      return this;    }    /**     * 设置上传回调     *     * @param listener     * @return     */    public Builder setListener(UploadTaskListener listener) {      this.listener = listener;      return this;    }    public UploadTask build() {      return new UploadTask(this);    }  }  private void addParams(MultipartBody.Builder builder, Map<String, String> params) {    if (params != null && !params.isEmpty()) {      for (String key : params.keySet()) {        builder.addPart(Headers.of("Content-Disposition", "form-data; name=/"" + key + "/""),            RequestBody.create(null, params.get(key)));      }    }  }}

UploadManager上传管理器

package com.mainaer.wjoklib.okhttp.upload;import android.content.Context;import android.database.sqlite.SQLiteDatabase; import java.util.HashMap;import java.util.Map;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.TimeUnit; import okhttp3.OkHttpClient; /** * 上传管理器 * * @author wangjian * @date 2016/5/13 . */public class UploadManager {   private static Context mContext;   private static SQLiteDatabase db;  private OkHttpClient mClient;   private int mPoolSize = 20;  // 将执行结果保存在future变量中  private Map<string, future=""> mFutureMap;  private ExecutorService mExecutor;  private Map<string, uploadtask=""> mCurrentTaskList;   static UploadManager manager;   /**   * 方法加锁,防止多线程操作时出现多个实例   */  private static synchronized void init() {    if (manager == null) {      manager = new UploadManager();    }  }   /**   * 获得当前对象实例   *   * @return 当前实例对象   */  public final static UploadManager getInstance() {    if (manager == null) {      init();    }    return manager;  }   /**   * 管理器初始化,建议在application中调用   *   * @param context   */  public static void init(Context context, SQLiteDatabase db1) {    mContext = context;    db = db1;    getInstance();  }   public UploadManager() {    initOkhttpClient();     // 初始化线程池    mExecutor = Executors.newFixedThreadPool(mPoolSize);    mFutureMap = new HashMap<>();    mCurrentTaskList = new HashMap<>();  }   /**   * 初始化okhttp   */  private void initOkhttpClient() {    OkHttpClient.Builder okBuilder = new OkHttpClient.Builder();    okBuilder.connectTimeout(1000, TimeUnit.SECONDS);    okBuilder.readTimeout(1000, TimeUnit.SECONDS);    okBuilder.writeTimeout(1000, TimeUnit.SECONDS);    mClient = okBuilder.build();  }   /**   * 添加上传任务   *   * @param uploadTask   */  public void addUploadTask(UploadTask uploadTask) {    if (uploadTask != null && !isUploading(uploadTask)) {      uploadTask.setClient(mClient);      uploadTask.setUploadStatus(UploadStatus.UPLOAD_STATUS_INIT);      // 保存上传task列表      mCurrentTaskList.put(uploadTask.getId(), uploadTask);      Future future = mExecutor.submit(uploadTask);      mFutureMap.put(uploadTask.getId(), future);    }  }   private boolean isUploading(UploadTask task) {    if (task != null) {      if (task.getUploadStatus() == UploadStatus.UPLOAD_STATUS_UPLOADING) {        return true;      }    }    return false;  }   /**   * 暂停上传任务   *   * @param id 任务id   */  public void pause(String id) {    UploadTask task = getUploadTask(id);    if (task != null) {      task.setUploadStatus(UploadStatus.UPLOAD_STATUS_PAUSE);    }  }   /**   * 重新开始已经暂停的上传任务   *   * @param id 任务id   */  public void resume(String id, UploadTaskListener listener) {    UploadTask task = getUploadTask(id);    if (task != null) {      addUploadTask(task);    }  } /*  *//**   * 取消上传任务(同时会删除已经上传的文件,和清空数据库缓存)   *   * @param id    任务id   * @param listener   *//*  public void cancel(String id, UploadTaskListener listener) {    UploadTask task = getUploadTask(id);    if (task != null) {      mCurrentTaskList.remove(id);      mFutureMap.remove(id);      task.setmListener(listener);      task.cancel();      task.setDownloadStatus(UploadStatus.DOWNLOAD_STATUS_CANCEL);    }  }*/   /**   * 实时更新manager中的task信息   *   * @param task   */  public void updateUploadTask(UploadTask task) {    if (task != null) {      UploadTask currTask = getUploadTask(task.getId());      if (currTask != null) {        mCurrentTaskList.put(task.getId(), task);      }    }  }   /**   * 获得指定的task   *   * @param id task id   * @return   */  public UploadTask getUploadTask(String id) {    UploadTask currTask = mCurrentTaskList.get(id);    if (currTask == null) {        currTask = parseEntity2Task(new UploadTask.Builder().build());        // 放入task list中        mCurrentTaskList.put(id, currTask);    }     return currTask;  }    private UploadTask parseEntity2Task(UploadTask currTask) {     UploadTask.Builder builder = new UploadTask.Builder()//        .setUploadStatus(currTask.getUploadStatus())        .setFileName(currTask.getFileName())//        .setUrl(currTask.getUrl())        .setId(currTask.getId());       currTask.setBuilder(builder);     return currTask;  }} 

FileUtils文件分块类

package com.mainaer.wjoklib.okhttp.upload;import java.io.File;import java.io.IOException;import java.io.RandomAccessFile;public class FileUtils {  public static byte[] getBlock(long offset, File file, int blockSize) {    byte[] result = new byte[blockSize];    RandomAccessFile accessFile = null;    try {      accessFile = new RandomAccessFile(file, "r");      accessFile.seek(offset);      int readSize = accessFile.read(result);      if (readSize == -1) {        return null;      } else if (readSize == blockSize) {        return result;      } else {        byte[] tmpByte = new byte[readSize];        System.arraycopy(result, 0, tmpByte, 0, readSize);        return tmpByte;      }    } catch (IOException e) {      e.printStackTrace();    } finally {      if (accessFile != null) {        try {          accessFile.close();        } catch (IOException e1) {        }      }    }    return null;  }}

UploadTaskListener 接口类

package com.mainaer.wjoklib.okhttp.upload; import com.mainaer.wjoklib.okhttp.download.DownloadStatus;import java.io.File;/** * Created by hst on 16/9/21. */public interface UploadTaskListener {  /**   * 上传中   *   * @param percent   * @param uploadTask   */  void onUploading(UploadTask uploadTask, String percent,int position)   /**   * 上传成功   *   * @param file   * @param uploadTask   */  void onUploadSuccess(UploadTask uploadTask, File file);   /**   * 上传失败   *   * @param uploadTask   * @param errorCode  {@link DownloadStatus}   */  void onError(UploadTask uploadTask, int errorCode,int position);     /**  * 上传暂停  *  * @param uploadTask  *  */  void onPause(UploadTask uploadTask); } 

源码下载:okhttpUpLoader_jb51.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表