Java分块上传文件_Java 文件分块上传客户端源代码

本博客介绍如何进行文件的分块上传。本文侧重介绍客户端,服务器端将在后续博客介绍。

所谓分块上传并非把大文件进行分块,然后挨个上传,而是依次读取大文件的一部分文件流进行上传。分块,倒不如说分流比较切实。本文通过一个项目中的示例,说明使用 Apache 的 HttpComponents/HttpClient 对大文件进行分块上传的过程。示例使用的版本是 HttpComponents Client 4.2.1。

本文仅以一小 demo 功能性地解释 HttpComponents/HttpClient 分块上传,没有考虑字节流关闭、多线程等资源因素,读者可以根据自己的项目酌情处理。

本文核心思想及流程:以 100 MB 大小为例,大于 100 MB 的进行分块上传,否则整块上传。对于大于 100 MB 的文件,又以 100 MB 为单位进行分割,保证每次以不大于 100 MB 的大小进行上传。比如 304 MB 的一个文件会分为 100 MB、100 MB、100 MB、4 MB 等四块依次上传。第一次读取 0 字节开始的 100 MB 个字节,上传;第二次读取第 100 MB 字节开始的 100 MB 个字节,上传;第三次读取第 200 MB 字节开始的 100 MB 个字节,上传;第四次读取最后剩下的 4 MB 个字节进行上传。

自定义的 ContentBody 源码如下,其中定义了流的读取和输出:

package com.defonds.rtupload.common.util.block;

import java.io.File;

import java.io.IOException;

import java.io.OutputStream;

import java.io.RandomAccessFile;

import org.apache.http.entity.mime.content.AbstractContentBody;

import com.defonds.rtupload.GlobalConstant;

public class BlockStreamBody extends AbstractContentBody {

//给MultipartEntity看的2个参数

private long blockSize = 0;//本次分块上传的大小

private String fileName = null;//上传文件名

//writeTo需要的3个参数

private int blockNumber = 0, blockIndex = 0;//blockNumber分块数;blockIndex当前第几块

private File targetFile = null;//要上传的文件

private BlockStreamBody(String mimeType) {

super(mimeType);

// TODO Auto-generated constructor stub

}

/**

* 自定义的ContentBody构造子

* @param blockNumber分块数

* @param blockIndex当前第几块

* @param targetFile要上传的文件

*/

public BlockStreamBody(int blockNumber, int blockIndex, File targetFile) {

this("application/octet-stream");

this.blockNumber = blockNumber;//blockNumber初始化

this.blockIndex = blockIndex;//blockIndex初始化

this.targetFile = targetFile;//targetFile初始化

this.fileName = targetFile.getName();//fileName初始化

//blockSize初始化

if (blockIndex < blockNumber) {//不是最后一块,那就是固定大小了

this.blockSize = GlobalConstant.CLOUD_API_LOGON_SIZE;

} else {//最后一块

this.blockSize = targetFile.length() - GlobalConstant.CLOUD_API_LOGON_SIZE * (blockNumber - 1);

}

}

@Override

public void writeTo(OutputStream out) throws IOException {

byte b[] = new byte[1024];//暂存容器

RandomAccessFile raf = new RandomAccessFile(targetFile, "r");//负责读取数据

if (blockIndex == 1) {//第一块

int n = 0;

long readLength = 0;//记录已读字节数

while (readLength <= blockSize - 1024) {//大部分字节在这里读取

n = raf.read(b, 0, 1024);

readLength += 1024;

out.write(b, 0, n);

}

if (readLength <= blockSize) {//余下的不足 1024 个字节在这里读取

n = raf.read(b, 0, (int)(blockSize - readLength));

out.write(b, 0, n);

}

} else if (blockIndex < blockNumber) {//既不是第一块,也不是最后一块

raf.seek(GlobalConstant.CLOUD_API_LOGON_SIZE * (blockIndex - 1));//跳过前[块数*固定大小 ]个字节

int n = 0;

long readLength = 0;//记录已读字节数

while (readLength <= blockSize - 1024) {//大部分字节在这里读取

n = raf.read(b, 0, 1024);

readLength += 1024;

out.write(b, 0, n);

}

if (readLength <= blockSize) {//余下的不足 1024 个字节在这里读取

n = raf.read(b, 0, (int)(blockSize - readLength));

out.write(b, 0, n);

}

} else {//最后一块

raf.seek(GlobalConstant.CLOUD_API_LOGON_SIZE * (blockIndex - 1));//跳过前[块数*固定大小 ]个字节

int n = 0;

while ((n = raf.read(b, 0, 1024)) != -1) {

out.write(b, 0, n);

}

}

//TODO 最后不要忘掉关闭out/raf

}

@Override

public String getCharset() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getTransferEncoding() {

// TODO Auto-generated method stub

return "binary";

}

@Override

public String getFilename() {

// TODO Auto-generated method stub

return fileName;

}

@Override

public long getContentLength() {

// TODO Auto-generated method stub

return blockSize;

}

}

在自定义的 HttpComponents/HttpClient 工具类 HttpClient4Util 里进行分块上传的封装:

public static String restPost(String serverURL, File targetFile,Map mediaInfoMap){

String content ="";

try {

DefaultHttpClient httpClient = new DefaultHttpClient();

HttpPost post = new HttpPost(serverURL +"?");

httpClient.getParams().setParameter("http.socket.timeout",60*60*1000);

MultipartEntity mpEntity = new MultipartEntity();

List keys = new ArrayList(mediaInfoMap.keySet());

Collections.sort(keys, String.CASE_INSENSITIVE_ORDER);

for (Iterator iterator = keys.iterator(); iterator.hasNext();) {

String key = iterator.next();

if (StringUtils.isNotBlank(mediaInfoMap.get(key))) {

mpEntity.addPart(key, new StringBody(mediaInfoMap.get(key)));

}

}

if(targetFile!=null&&targetFile.exists()){

ContentBody contentBody = new FileBody(targetFile);

mpEntity.addPart("file", contentBody);

}

post.setEntity(mpEntity);

HttpResponse response = httpClient.execute(post);

content = EntityUtils.toString(response.getEntity());

httpClient.getConnectionManager().shutdown();

} catch (Exception e) {

e.printStackTrace();

}

System.out.println("=====RequestUrl==========================\n"

+getRequestUrlStrRest(serverURL, mediaInfoMap).replaceAll("&fmt=json", ""));

System.out.println("=====content==========================\n"+content);

return content.trim();

}

其中 "file" 是分块上传服务器对分块文件参数定义的名字。

最后调用 HttpClient4Util 进行上传:

public static Map uploadToDrive(

Map params, String domain) {

File targetFile = new File(params.get("filePath"));

long targetFileSize = targetFile.length();

int mBlockNumber = 0;

if (targetFileSize < GlobalConstant.CLOUD_API_LOGON_SIZE) {

mBlockNumber = 1;

} else {

mBlockNumber = (int) (targetFileSize / GlobalConstant.CLOUD_API_LOGON_SIZE);

long someExtra = targetFileSize

% GlobalConstant.CLOUD_API_LOGON_SIZE;

if (someExtra > 0) {

mBlockNumber++;

}

}

params.put("blockNumber", Integer.toString(mBlockNumber));

if (domain != null) {

LOG.debug("Drive---domain=" + domain);

LOG.debug("drive---url=" + "http://" + domain + "/sync"

+ GlobalConstant.CLOUD_API_PRE_UPLOAD_PATH);

} else {

LOG.debug("Drive---domain=null");

}

String responseBodyStr = HttpClient4Util.getRest("http://" + domain

+ "/sync" + GlobalConstant.CLOUD_API_PRE_UPLOAD_PATH, params);

ObjectMapper mapper = new ObjectMapper();

DrivePreInfo result;

try {

result = mapper.readValue(responseBodyStr, ArcDrivePreInfo.class);

} catch (IOException e) {

LOG.error("Drive.preUploadToArcDrive error.", e);

throw new RtuploadException(GlobalConstant.ERROR_CODE_13001);// TODO

}

// JSONObject jsonObject = JSONObject.fromObject(responseBodyStr);

if (Integer.valueOf(result.getRc()) == 0) {

int uuid = result.getUuid();

String upsServerUrl = result.getUploadServerUrl().replace("https",

"http");

if (uuid != -1) {

upsServerUrl = upsServerUrl

+ GlobalConstant.CLOUD_API_UPLOAD_PATH;

params.put("uuid", String.valueOf(uuid));

for (int i = 1; i <= mBlockNumber; i++) {

params.put("blockIndex", "" + i);

HttpClient4Util.restPostBlock(upsServerUrl, targetFile,

params);//

}

}

} else {

throw new RtuploadException(GlobalConstant.ERROR_CODE_13001);// TODO

}

return null;

}

其中 params 这个 Map 里封装的是服务器分块上传所需要的一些参数。

本文中的示例虽经本人测试能够上传文件成功,如果读者朋友测试时遇到问题无法上传成功,请在博客后跟帖留言,大家共同交流下。本文示例肯定还存在很多不足之处,如果读者朋友发现还请留言指出,笔者先行谢过了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值