了解HttpClient
- HttpClient是一个用来模拟浏览器发送http请求的java实现。我们可以通过编写java代码来自己发送http请求来获取接口的返回值。这次我参与的项目主要是在安卓端的时候调用上传文件至服务器用到。
/**
* 多线程分片上传文件
*
* @param url 服务器接收路劲
* @param filePath 当前的文件地址
* @return
* @throws IOException
*/
public static String fileChunkUpLoadAsync(String url, String checkUrl,String filePath, @Nullable Runnable allCallback) throws ExecutionException, InterruptedException {
long timestamp = System.currentTimeMillis();
System.out.println("文件上传开始时间:" + timestamp);
File targetFile = new File(filePath);
//文件大小
long targetFileSize = targetFile.length();
//分片总数
int chunks = number(targetFileSize);
String md5 = getFileMd5(filePath);
JsonResponse jr = checkFileExists(checkUrl,md5);
if(jr.getStatus()){
if(null != allCallback){
allCallback.run();
}
return jr.getContent();
}
Thread t = new Thread(() -> {
ExecutorService executorService = new ThreadPoolExecutor(chunks<=3?1:chunks<=10?2:chunks<=50?3:chunks<=100?4:5,
chunks,
9999,
TimeUnit.DAYS,
new LinkedBlockingDeque<>(),
(ThreadFactory) Thread::new);
int index;
for (index = 1; index <= chunks; index++) {
UploadThreadRun utr=new UploadThreadRun(url, targetFile, chunks, index, md5);
executorService.submit(utr);
}
executorService.shutdown();
if (allCallback != null) {
try {
executorService.awaitTermination(9999, TimeUnit.HOURS);
} catch (InterruptedException e) {
e.printStackTrace();
}
allCallback.run();
}
});
t.start();
return jr.getContent();
}
/**
* 根据文件的大小 获取该文件分片数
*
* @param targetFileSize
* @return
*/
public static int number(long targetFileSize) {
int mBlockNumber = 0;
// 小于要分割的大小就是一块
if (targetFileSize < ApplicationGlobalVariables.ApplicationConfig.getDefaultChunkSize()) {
mBlockNumber = 1;
} else {
mBlockNumber = (int) (targetFileSize / ApplicationGlobalVariables.ApplicationConfig.getDefaultChunkSize());
long someExtra = targetFileSize % ApplicationGlobalVariables.ApplicationConfig.getDefaultChunkSize();
// 剩下的不足一块要加1
if (someExtra > 0) {
mBlockNumber++;
}
}
return mBlockNumber;
}
/**
* 获取文件的MD5值
*
* @param filePath
* @return
*/
public static String getFileMd5(String filePath) {
String md5 = "";
try {
md5 = DigestUtils.md5Hex(new FileInputStream(filePath));
} catch (IOException e) {
e.printStackTrace();
}
return md5;
}
- UploadThreadRun
package base.thread;
import base.util.DateUtils;
import core.ApplicationGlobalVariables;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
public class UploadThreadRun implements Runnable {
public static final Logger logger = LoggerFactory.getLogger(UploadThreadRun.class);
//上传到服务器的URL
String serverURL;
//需要上传文件
File targetFile;
//总的分片数
int chunks;
//当前分片
int chunk;
//本机的文件地址 /文件夹上传的时候用到
String localFilePath;
//当前分片的MD5
String chunkMd5;
//总文件的MD5
String md5;
//在队列中的位置 文件队列
int il;
//文件个数
int fileNumber;
public UploadThreadRun(String serverURL, File targetFile, int chunks, int chunk,String md5) {
this.serverURL = serverURL;
this.targetFile = targetFile;
this.chunks = chunks;
this.chunk = chunk;
this.md5 = md5;
this.localFilePath = "";
this.il = 1;
this.fileNumber = 1;
}
@Override
public void run() {
long begin = System.currentTimeMillis();
System.out.println("++++++++++++++++");
if(fileNumber>1){
if(chunks>1){
System.out.println("多文件分片上传 共需上传文件:"+fileNumber+"个,当前文件在列表中的位置:第"+il+"个,共"+chunks+"片,第"+chunk+"片,该文件路径"+localFilePath);
}else{
System.out.println("多文件上传 共需上传文件:"+fileNumber+"个,当前文件在列表中的位置:"+il+",该文件路径"+localFilePath);
}
}else{
if(chunks>1){
System.out.println("单文件上传 共"+chunks+"片,第"+chunk+"片,该文件路径"+localFilePath);
}else{
System.out.println("单文件上传 该文件路径"+localFilePath);
}
}
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httppost = new HttpPost(serverURL);
chunkMd5="";
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("chunks",String.valueOf(chunks))
.addTextBody("chunk",String.valueOf(chunk))
.addTextBody("chunkMd5",chunkMd5)
.addTextBody("md5",md5);
if(targetFile != null && targetFile.exists()) {
byte[] data=getByte(chunks,chunk, ApplicationGlobalVariables.ApplicationConfig.getDefaultChunkSize(),targetFile);
builder.addBinaryBody("file",data,ContentType.DEFAULT_BINARY,targetFile.getName());
}
//设置文件名编码
builder.setCharset(Charset.forName("utf-8"));
//兼容模式
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
HttpEntity reqEntity = builder.build();
httppost.setEntity(reqEntity);
HttpResponse response = httpClient.execute(httppost);
if(response.getStatusLine().getStatusCode()==200){
long end = System.currentTimeMillis();
logger.debug(targetFile.getPath()+"耗时"+ DateUtils.countTime2String(begin,end));
} else{
long end = System.currentTimeMillis();
logger.debug(targetFile.getPath()+"耗时"+DateUtils.countTime2String(begin,end));
logger.error(response.getStatusLine().getStatusCode()+targetFile.getPath());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] getByte(int chunks,int chunk,long chunkSize,File targetFile){
byte[] r=null;
try(RandomAccessFile raf = new RandomAccessFile(targetFile, "r")){
if(chunk==chunks){
long begin=(chunk-1)*chunkSize;
if(raf.length()-begin>0){
byte[] data=new byte[(int)(raf.length()-begin)];
raf.seek(begin);
raf.read(data);
r=data;
}
}else {
byte[] data = new byte[(int) chunkSize];
raf.seek((chunk - 1) * chunkSize);
raf.read(data);
r=data;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return r;
}
}