android中okhttp实现断点上传示例

前言

之前项目需要上传大文件的功能,上传大文件经常遇到上传一半由于网络或者其他一些原因上传失败。然后又得重新上传(很麻烦),所以就想能不能做个断点上传的功能。于是网上搜索,发现市面上很少有断点上传的案例,有找到一个案例也是采用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

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

时间: 2024-08-02 00:51:02

android中okhttp实现断点上传示例的相关文章

Android中Socket大文件断点上传示例

什么是Socket? 所谓Socket通常也称作"套接字",用于描述IP地址和端口,是一个通信连的句柄,应用程序通常通过"套接字"向网络发送请求或者应答网络请求,它就是网络通信过程中端点的抽象表示.它主要包括以下两个协议: TCP (Transmission Control Protocol 传输控制协议):传输控制协议,提供的是面向连接.可靠的字节流服务.当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据.TCP提供超时重发,丢弃重

Android中使用HTTP服务上传文件

http://blog.csdn.net/liuhe688/article/details/6425225 在Android中,除了使用java.net包下的API访问HTTP服务之外,我们还可以换一种途径去完成工作.Android SDK附带了Apache的HttpClient API.Apache HttpClient是一个完善的HTTP客户端,它提供了对HTTP协议的全面支持,可以使用HTTP GET和POST进行访问.下面我们就结合实例,介绍一下HttpClient的使用方法. 我们新建

Asp.Net中FileUpload实现文件上传示例

属性:FileName: 获取上传的文件名 HasFile: 是否选择(存在)上传的文件 ContentLength: 获得上窜文件的大小,单位是字节(byte)   方法:Server.MapPath(): 获取服务器上的物理路径 SaveAs(): 保存文件到指定的文件夹   注意:默认情况下限制上传文件大小为4MB,通过web.config.comments(这个设置是全局的配置)可以修改其默认设置 或者通过修改web.config文件来改变应用程序上传限制.   如: 1 <httpRu

android自定义ImageView仿图片上传示例

看下效果图 主要看下自定义view 代码 public class ProcessImageView extends ImageView{ private Context context; private Paint paint; private LogUtil log=LogUtil.getInstance(); int progress = 0; private boolean flag; public ProcessImageView(Context context) { super(co

Android中如何实现Socket大文件断点上传

什么是Socket? 所谓Socket通常也称作"套接字",用于描述IP地址和端口,是一个通信连的句柄,应用程序通常通过"套接字"向网络发送请求或者应答网络请求,它就是网络通信过程中端点的抽象表示.它主要包括以下两个协议: TCP (Transmission Control Protocol 传输控制协议):传输控制协议,提供的是面向连接.可靠的字节流服务.当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据.TCP提供超时重发,丢弃重

Android应用开发之使用Socket进行大文件断点上传续传

在Android中上传文件可以采用HTTP方式,也可以采用Socket方式,但是HTTP方式不能上传大文件,这里介绍一种通过Socket方式来进行断点续传的方式,服务端会记录下文件的上传进度,当某一次上传过程意外终止后,下一次可以继续上传,这里用到的其实还是J2SE里的知识. 这个上传程序的原理是:客户端第一次上传时向服务端发送"Content-Length=35;filename=WinRAR_3.90_SC.exe;sourceid="这种格式的字符串,服务端收到后会查找该文件是否

ios socket 断点上传-iOS怎么用socket实现大文件断点上传功能,类似于优酷中视频上传?

问题描述 iOS怎么用socket实现大文件断点上传功能,类似于优酷中视频上传? iOS怎么用socket实现大文件断点上传功能,类似于优酷中视频上传?哪位大神给点思路或者相关demo,跪求!!! 解决方案 Android中Socket大文件断点上传Android中Socket大文件断点上传Android中Socket大文件断点上传---------------------- 解决方案二: 一遍这种都是,两边定义好命令, 文件拆分成一块一块的,每一块有一个序号,断点续传就是没上传那块接着传就行了

Android实现TCP断点上传 后台C#服务接收_Android

终端实现大文件上传一直都是比较难的技术,其中涉及到后端与前端的交互,稳定性和流量大小,而且实现原理每个人都有自己的想法,后端主流用的比较多的是Http来实现,因为大多实现过断点下载.但稳定性不能保证,一旦断开,无法续传.所以得采用另一种流行的做法,TCP上传大文件.  网上查找了一些资料,大多数是断点下载,然后就是单独的C#端的上传接收,或是HTTP的,或是只有android端的,由于任务紧所以之前找的首选方案当然是Http先来实现文件上传,终端采用Post方法,将文件直接传至后端,后端通过Fi

Android编程实现图片的上传和下载功能示例_Android

本文实例讲述了Android编程实现图片的上传和下载功能.分享给大家供大家参考,具体如下: 在实现一个Android的WEB服务客户端,比如微博,论坛客户端时,经常会使用到图片的上传和下载.在这里介绍如何利用HttpClient实现图片的上传和下载功能. 1 图片上传:上传图片时,首先获得图片的路径,创建文件,并将图片转化为字节流写入到request,并发送该请求. 客户端代码: File file = new File(imageUrl); String httpUrl = httpDomai