基于HttpClient的多线程上传

了解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;
}
  1. 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;
}

}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容