JAVA 实现多线程下载大文件

开发中遇到一个问题,下载大文件到本地,导致等待时间过长,然后就寻找一个可以多线程下载的办法,受下载软件启发,想到多线程下载,
原理,首先查到这个文件的大小,然后根据线程数量去分配每个线程下载多大的片段,然后将每个线程的组合到一起,就是最终的下载文件。如图

然后就是代码时间, 必不可少的控制层

    @Override@ApiOperation(value = "多线程获取大文件", httpMethod = "POST")@BodyValidatepublic Result getBigFile(@RequestBody CommonFileRequest commonFileRequest, HttpServletResponse response) {return commonFileService.getBigFile(commonFileRequest,response);}

然后是业务层

 /*** .* @Description: 作用:   多线程获取大文件* @Author: LXT* @Date: 2021/12/27 9:18* @param request 入参* @return Result*/public Result getBigFile(CommonFileRequest request, HttpServletResponse response) {logger.info("多线程获取大文件," + JSON.toJSONString(request));long start = System.currentTimeMillis();//超大  6G//String path = "???";// 1.3G//String path = "???";//正常大小String path = "???";try {ServletOutputStream out = null;String s = BigFileDownload.BigFileTestUtil(accessKey, secretKey, endpoint, bucketName, path);File file = new File(s);InputStream fis = new BufferedInputStream(new FileInputStream(file));response.reset();// 设置response的Headerresponse.addHeader("Content-Length", "" + file.length());response.setContentType("application/octet-stream;charset=UTF-8");response.setHeader("Content-disposition", "attachment;filename=" + UUID.randomUUID().toString().replace("-", "") + ".zip");out = response.getOutputStream();//读取文件流int len = 0;byte[] buffer = new byte[1024 * 10];while ((len = fis.read(buffer)) != -1){out.write(buffer,0,len);}out.flush();} catch (Exception ex) {ex.printStackTrace();}logger.info("获取大文件耗时 {}", BusinessStringUtils.formatTime(System.currentTimeMillis() - start));return null;}

解析 首先 记录一下请求的日志 然后是拿到对应的存储位置
然后去平台获取文件 BigFileDownload.BigFileTestUtil 这个方法
返回的缓存在服务器 或者本地的一个位置 然后将其返回给移动端
后边这些就不说了 都是正常的反流代码 这里着重说明 多线程分片下载


/*** .** @ClassName: BigFileDownload* @Description:* @Author: LXT* @Date: 2021/12/27 13:01*/
public class BigFileDownload {//创建一个计数器锁。初始值为线程数量,每执行结束一个线程后计数器减去1 ,当计数器为0的时候await等待的线程会被唤醒继续执行。public static CountDownLatch latch = new CountDownLatch(100);public static String BigFileTestUtil(String accessKey, String secretKey, String endpoint, String bucketName, String downloadFilePath) {RandomAccessFile file = null;String filename = "D:\\222ddddd2.zip";File fileDownService = new File(filename);if (!fileDownService.exists()) {try {fileDownService.createNewFile();} catch (IOException e) {e.printStackTrace();}System.out.println("文件已创建");} else {System.out.println("文件已存在");}try {AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);ClientConfiguration clientConfig = new ClientConfiguration();clientConfig.setProtocol(Protocol.HTTP);AmazonS3 s3Client = new AmazonS3Client(credentials, clientConfig);s3Client.setEndpoint(endpoint);//获取对象大小AmazonS3 instance = AmazonS3Builder.getInstance(endpoint, accessKey, secretKey);S3Object object = instance.getObject(bucketName, downloadFilePath);S3ObjectInputStream in3 = object.getObjectContent();ObjectMetadata metadata = object.getObjectMetadata();long filesize = metadata.getInstanceLength();System.out.println("文件大小:" + filesize);ExecutorService service = Executors.newFixedThreadPool(100);long length = filesize;long packageLength = length / 100;long leftLength = length % 100;long pos = 0;long end = packageLength;file = new RandomAccessFile(filename, "rw");//计算每个线程请求文件的开始和结束位置for (int i = 0; i < 100; i++) {if (leftLength > 0) {packageLength++;leftLength--;}System.out.println("pos: " + pos + "  endpos: " + packageLength);service.execute(new BigFileDownloadRunnable(pos, packageLength, file, endpoint, accessKey, secretKey, bucketName, downloadFilePath));pos = packageLength;packageLength = packageLength + end;}//等待其他线程结束后继续向下执行try {latch.await();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}//关闭线程池service.shutdown();} catch (AmazonServiceException | FileNotFoundException e) {// 服务端错误e.printStackTrace();} finally {if (file != null) {try {file.close();} catch (Exception e) {}}}return filename;}}

其实也比较好理解 首先创建一个线程 用来分片下载
然后是主代码
方法入参 分别是 对象平台的一些参数 只有随后一个是存储平台地址 其他平台可自行更改
然后就是做一个本地缓存用的假文件
接着到 异常里
先做存储平台的链接,然后获取要下载文件的大小
获取大小后 更具线程数量 自行计算启动多少个线程来下载 我这里直接给的100 然后使用for进行线程的启动 启动后进入线程等待 全执行完之后 可正常生成文件,

然后就是线程使用的分片下载工具类


/*** .** @ClassName: BigFileDownloadRunnable* @Description:* @Author: LXT* @Date: 2021/12/27 13:19*/class BigFileDownloadRunnable implements Runnable {private String endpoint;private String accessKey;private String secretKey;private String bucketName;private long from;private long end;private RandomAccessFile file;private String downloadFilePath;public BigFileDownloadRunnable(long from, long end, RandomAccessFile file,String endpoint, String accessKey, String secretKey, String bucketName,String downloadFilePath) {this.from = from;this.end = end;this.file = file;this.endpoint = endpoint;this.accessKey = accessKey;this.secretKey = secretKey;this.bucketName = bucketName;this.downloadFilePath = downloadFilePath;}public void run() {String filename = downloadFilePath;S3Object objectPortion = null;InputStream input = null;BufferedInputStream buffer = null;try {AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);ClientConfiguration clientConfig = new ClientConfiguration();clientConfig.setProtocol(Protocol.HTTP);clientConfig.setConnectionTimeout(90000000);clientConfig.setMaxConnections(1000);AmazonS3 s3Client = new AmazonS3Client(credentials, clientConfig);s3Client.setEndpoint(endpoint);//获取对象指定范围的流写入文件GetObjectRequest rangeObjectRequest = new GetObjectRequest(bucketName, filename).withRange(from, end);objectPortion = s3Client.getObject(rangeObjectRequest);input = objectPortion.getObjectContent();buffer = new BufferedInputStream(input);byte[] buf = new byte[1024];int len;long start = this.from;long stop = this.end;for (; ; ) {if ((len = buffer.read(buf)) == -1) {break;}synchronized (file) {file.seek(from);file.write(buf, 0, len);}from += len;}System.out.println("文件片段 " + start + "~" + stop + "下载完成");new BigFileDownload().latch.countDown();//线程结束计数器减1} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if (buffer != null) {try {buffer.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if (input != null) {try {input.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if (objectPortion != null) {try {objectPortion.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}

因为线程分段 也需要去请球平台 所以就将平台的数据一并传输过来

然后线程内run 大概意思 就是 我拿到了平台链接 然后又有文件的存错位置 还有我需要下载的片段位置,然后就是正常下载过程

由于大文件比较大 链接时间设置的相对较长一点,

一小点的进步都是对开发者的激励,记录一下,分享给大家。有用记得点♥

JAVA 实现多线程下载大文件相关推荐

  1. Android 开发工具类 27_多线程下载大文件

    多线程下载大文件时序图 FileDownloader.java 1 package com.wangjialin.internet.service.downloader; 2 3 import jav ...

  2. Java8环境下使用restTemplate单/多线程下载大文件和小文件

    Java8环境下使用restTemplate单/多线程下载大文件和小文件 0. 准备工作 1. 简单的下载文件 2. 单线程大文件下载 3. 多线程下载 0. 准备工作 下面使用的restTempla ...

  3. libcurl使用多线程下载大文件源码示例!

    使用libcurl多线程下载大文件的基本思想: 首选打开文件,将文件等分为指定的片段,使用http range下载,一个线程下载一个片段,当线程下载片段时,它们将数据写到打开文件的指定位置,类似BT文 ...

  4. python多线程下载大文件_Python threading多线程断点下载文件的方法

    这是玩蛇网一篇关于Python多线程下载文件方法的代码实例.文中应用到的python模块和方法有httplib.Python urllib2.Python threading多线程模块.python ...

  5. Java实现FTP批量大文件上传下载

    用Java实现FTP批量大文件上传下载 <iframe id="I0_1416224567509" style="margin: 0px; padding: 0px ...

  6. 统计APP下载量--发现安卓下载大文件使用多线程下载方式

    问题产生 在项目开发过程中,会有各种各样的需求. 而在需求开发过程中,我们常常会遇到从没有见过的问题,这就需要我们一步一步排查. 在一次版本需求中,需要统计不同渠道APP的下载量. 理想很丰满,现实很 ...

  7. Java 使用 FTP 实现大文件上传下载

    Java 上传下载 1G 以上的文件可以通过 http 协议或 ftp 实现,但是 http 协议对文件上传大小有限制,而且还不稳定,因此这里使用 ftp 上传. ftp 上传方式有两种: 一.ASC ...

  8. 某盘提速下载工具Proxyee-down快速下载大文件

    点击上方"编程精选",选择"置顶公众号" 技术文章第一时间送达! 某网盘下载大文件一直是一个痛点.现在国内基本上只有百度网盘可用了,但是免费用户使用它下载东西的 ...

  9. python下载大文件-使用请求在python中下载大文件

    请求是一个非常好的库.我想用它下载大文件(>1GB).问题是不可能将整个文件保存在内存中,我需要将其分块读取.以下代码有问题 1 2 3 4 5 6 7 8 9 10 11import requ ...

最新文章

  1. 深度优先搜索算法在RPG游戏迷宫中的应用
  2. LeetCode(69):x 的平方根
  3. linux检查网络是否通畅_网络基础Ping命令详解(使用Ping这命令来测试网络连通)...
  4. ANN:DNN结构演进History—LSTM网络
  5. Laravel 使用 Aliyun OSS 云存储
  6. 44 FI配置-财务会计-固定资产-一般评估-指定购置和生产成本值的转移
  7. UVA260 Il Gioco dell‘X【DFS】
  8. html中图片阴影怎么写,css如何给图片加阴影?
  9. 用户控件中得到CurrentUser
  10. python全局变量的修改 线程共享全局变量
  11. git合并分支,发布代码
  12. C# 修改打印机名称
  13. mx250显卡天梯图_MX250相当于什么显卡?来看显卡天梯图
  14. Redis 在新浪微博中的应用
  15. Kali学习 | 漏洞扫描:3.1 Nessus安装、配置和新建扫描任务
  16. 低学历者已无法生存 程序员尤其明显
  17. 图片无损压缩工具(报名照片压缩至30Kb以下
  18. 常微分方程的数值解-欧拉、四阶龙格-库塔法等C语言
  19. mysql虚拟主机_虚拟主机有mysql吗
  20. 超越云存储,用一勺蛋白质保存整个图书馆

热门文章

  1. 兔年大吉丨云和恩墨恭祝您新春快乐!
  2. 基于官方开源Wine7.22完美使用通达信、微信软件
  3. 2015-2016年Unity项目经历
  4. 连接共享打印机了报0x00000709
  5. Scratch游戏“灰色梦魇”游戏介绍
  6. bugku-web-社工-初步收集
  7. C++ 制作内容检索程序
  8. 仙剑奇侠传3 物品详细介绍
  9. Jquery UI sortable
  10. 2019美亚杯团队赛