前言

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

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 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 mFutureMap;

private ExecutorService mExecutor;

private Map 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);

}

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

安卓okhttp连接mysql_android中okhttp实现断点上传示例相关推荐

  1. android学习笔记---32_文件断点上传器,解决多用户并发,以及自定义协议,注意协议中的漏洞

    32_文件断点上传器 --------------------------- 1.当文件很大的时候就无法通过http协议进行上传了,因为get,post的安全原因,很多服 务器会   禁止这些协议,而 ...

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

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

  3. JS 超大文件上传解决方案:分片断点上传(一)

    之前仿造uploadify写了一个HTML5版的文件上传插件,没看过的朋友可以点此先看一下~得到了不少朋友的好评,我自己也用在了项目中,不论是用户头像上传,还是各种媒体文件的上传,以及各种个性的业务需 ...

  4. 超大文件上传解决方案:分片断点上传

    之前仿造uploadify写了一个HTML5版的文件上传插件,没看过的朋友可以点此先看一下~得到了不少朋友的好评,我自己也用在了项目中,不论是用户头像上传,还是各种媒体文件的上传,以及各种个性的业务需 ...

  5. asp.net 如何实现大文件断点上传功能?

    之前仿造uploadify写了一个HTML5版的文件上传插件,没看过的朋友可以点此先看一下~得到了不少朋友的好评,我自己也用在了项目中,不论是用户头像上传,还是各种媒体文件的上传,以及各种个性的业务需 ...

  6. java断点上传下载_java实现多线程断点续传,上传下载 分享

    程序采用的ftp工具, apache 的 commons-net-ftp-ftpclient package com.ftp; import java.io.File; import java.io. ...

  7. java 断点上传_java HTTP文件断点上传

    之前仿造uploadify写了一个HTML5版的文件上传插件,没看过的朋友可以点此先看一下~得到了不少朋友的好评,我自己也用在了项目中,不论是用户头像上传,还是各种媒体文件的上传,以及各种个性的业务需 ...

  8. Akka实战:HTTP大文件断点上传、下载,秒传

    2019独角兽企业重金招聘Python工程师标准>>> 访问:https://github.com/yangbajing/scala-applications/tree/master ...

  9. java web 断点上传_使用WebUploader实现分片断点上传文件功能(二)

    写在前面: 这几天,有去研究一下WebUploader上传文件,前面的博客有记录下使用WebUploader简单上传文件的例子,今天就把分片断点上传的例子也记录下吧,在博客园中,也查看了一些资料,基本 ...

最新文章

  1. yum源快速配置脚本
  2. Windows 10下安装scrapy(pip方式,非wheel)
  3. 原版英文书籍《Linux命令行》阅读记录4 | 操作文件和目录
  4. ros android 方案,ros android_core学习笔记
  5. c 不安装oracle,安装oracle 10g 的艰难之旅
  6. Oracle如何实现跨库查询
  7. 计算机组成原理累加器实验,计算机组成原理实验教学的探讨.doc
  8. 一个基本开发框架的整合演化之路--9、整合文件服务器fastdfs
  9. 通达信插件模板 Purebasic版
  10. 利用windows 自带WiFi共享工具共享WiFi
  11. 小程序公众图标素材+6113个菜单栏素材
  12. PR 2019 快速入门(8)
  13. cents OS 重装yum,配置阿里yum源
  14. python感叹号的作用_Python的作用
  15. 弘辽科技:新手前期如何开网店?
  16. 周易六十四卦——雷火丰卦
  17. C语言22选5体育彩票系统,22选5选号方法大全(二)
  18. webgl-原生纹理贴图
  19. WordPress主题柒比贰v2.9.8 完美无限制版
  20. ZigBee的电子标签系统设计

热门文章

  1. 蓝色巨人将磁带定位为数据存储的集成归档层
  2. 2018 German Collegiate Programming Contest (GCPC 18)
  3. 特斯拉与Mobileye口水战的背后,是自动驾驶技术话语权之争
  4. snoopy(强大的PHP采集类) 实例应用
  5. tomcat服务器介绍之二 、session服务器实现方法
  6. rsync同步文件到远程主机
  7. 提高PHP性能的53个技巧
  8. GRUB2 管理器—Grub Customizer
  9. Win XP系统的开机菜单具体含义
  10. 【转】ABAP内表数据和JSON格式互转