java实现大文件下载(http方式)

java实现大文件下载,基于http方式,控件神马的就不说了。

思路:下载文件无非要读取文件然后写文件,主要这两个步骤,主要难点:

    1.读文件,就是硬盘到内存的过程,由于jdk内存限制,不能读的太大。

    2.写文件,就是响应到浏览器端的过程,http协议是短链接,如果写文件太慢,时间过久,会造成浏览器死掉。

知识点:

    1.org.apache.http.impl.client.CloseableHttpClient  模拟httpClient客户端发送http请求,可以控制到请求文件的字节位置。

    2.BufferedInputStream都熟悉,用它接受请求来的流信息缓存。

    3.RandomAccessFile文件随机类,可以向文件写入指定位置的流信息。

基于以上信息,我的实现思路就是首先判断下载文件大小,配合多线程分割定制http请求数量和请求内容,响应到写入到RandomAccessFile指定位置中。在俗点就是大的http分割成一个个小的http请求,相当于每次请求一个网页。

废话不说,上代码。


DownLoadManagerTest类:

1package xxxx; 2 3import java.io.File; 4import java.io.IOException; 5import java.io.RandomAccessFile; 6import java.net.HttpURLConnection; 7import java.net.URL; 8import java.util.concurrent.CountDownLatch; 9import org.apache.commons.lang.exception.ExceptionUtils; 10import org.apache.http.impl.client.CloseableHttpClient; 11import org.apache.http.impl.client.HttpClients; 12import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 13import org.junit.After; 14import org.junit.Before; 15import org.junit.Test; 16import org.junit.runner.RunWith; 17import org.slf4j.Logger; 18import org.slf4j.LoggerFactory; 19import org.springframework.beans.factory.annotation.Autowired; 20import org.springframework.core.task.TaskExecutor; 21import org.springframework.test.context.ActiveProfiles; 22import org.springframework.test.context.ContextConfiguration; 23import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; 24import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 25 26/** 27 *  28 * 文件下载管理类 29 */ 30 31@RunWith(SpringJUnit4ClassRunner.class) 32@ActiveProfiles("test") 33@ContextConfiguration(locations={"classpath:test/applicationContext.xml"}) 34public class DownLoadManagerTest extends AbstractTransactionalJUnit4SpringContextTests{ 35 36 private static final Logger LOGGER = LoggerFactory.getLogger(DownLoadManagerTest.class); 37 38 /** 39  *  40  * 每个线程下载的字节数 41  */ 42 43 private long unitSize = 1000 * 1024; 44 45 @Autowired 46 private TaskExecutor taskExecutor; 47 48 private CloseableHttpClient httpClient; 49 50 private Long starttimes; 51 52 private Long endtimes; 53     54    @Before 55    public void setUp() throws Exception 56    { 57     starttimes = System.currentTimeMillis(); 58        System.out.println("测试开始...."); 59    } 60     61    @After 62    public void tearDown() throws Exception 63    { 64     endtimes = System.currentTimeMillis(); 65        System.out.println("测试结束!!"); 66        System.out.println("********************"); 67        System.out.println("下载总耗时:"+(endtimes-starttimes)/1000+"s"); 68        System.out.println("********************"); 69    } 70 71 public DownLoadManagerTest() { 72 73 System.out.println("初始化测试类...."); 74 PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); 75 cm.setMaxTotal(100); 76 httpClient = HttpClients.custom().setConnectionManager(cm).build(); 77 78 } 79 80 /** 81  *  82  * 启动多个线程下载文件 83  */ 84 @Test 85 public void  doDownload() throws IOException { 86 87 String remoteFileUrl="http://{host}:{port}/{project}/xx.xml"; 88 String localPath="E://test//"; 89 90 String fileName = new URL(remoteFileUrl).getFile(); 91 92 System.out.println("远程文件名称:"+fileName); 93 fileName = fileName.substring(fileName.lastIndexOf("/") + 1, 94 fileName.length()).replace("%20", " "); 95 System.out.println("本地文件名称:"+fileName); 96 long fileSize = this.getRemoteFileSize(remoteFileUrl); 97 98 this.createFile(localPath+System.currentTimeMillis()+fileName, fileSize); 99 100 Long threadCount = (fileSize/unitSize)+(fileSize % unitSize!=0?1:0); 101 long offset = 0; 102 103 CountDownLatch end = new CountDownLatch(threadCount.intValue()); 104 105 if (fileSize <= unitSize) {// 如果远程文件尺寸小于等于unitSize 106 107 DownloadThreadTest downloadThread = new DownloadThreadTest(remoteFileUrl, 108 109 localPath+fileName, offset, fileSize,end,httpClient); 110 111 taskExecutor.execute(downloadThread); 112 113 } else {// 如果远程文件尺寸大于unitSize 114 115 for (int i = 1; i < threadCount; i++) { 116 117 DownloadThreadTest downloadThread = new DownloadThreadTest( 118 119 remoteFileUrl, localPath+fileName, offset, unitSize,end,httpClient); 120 121 taskExecutor.execute(downloadThread); 122 123 offset = offset + unitSize; 124 125 } 126 127 if (fileSize % unitSize != 0) {// 如果不能整除,则需要再创建一个线程下载剩余字节 128 129 DownloadThreadTest downloadThread = new DownloadThreadTest(remoteFileUrl, localPath+fileName, offset, fileSize - unitSize * (threadCount-1),end,httpClient); 130 taskExecutor.execute(downloadThread); 131 } 132 133 } 134 try { 135 end.await(); 136 } catch (InterruptedException e) { 137 LOGGER.error("DownLoadManager exception msg:{}",ExceptionUtils.getFullStackTrace(e)); 138 e.printStackTrace(); 139 } 140// System.out.println("111111"); 141 LOGGER.debug("下载完成!{} ",localPath+fileName); 142 //return localPath+fileName; 143 } 144 145 /** 146  *  147  * 获取远程文件尺寸 148  */ 149 150 private long getRemoteFileSize(String remoteFileUrl) throws IOException { 151 152 long fileSize = 0; 153 154 HttpURLConnection httpConnection = (HttpURLConnection) new URL( 155 156 remoteFileUrl).openConnection(); 157 158 httpConnection.setRequestMethod("HEAD"); 159 160 int responseCode = httpConnection.getResponseCode(); 161 162 if (responseCode >= 400) { 163 164 LOGGER.debug("Web服务器响应错误!"); 165 166 return 0; 167 168 } 169 170 String sHeader; 171 172 for (int i = 1;; i++) { 173 174 sHeader = httpConnection.getHeaderFieldKey(i); 175 176 if (sHeader != null && sHeader.equals("Content-Length")) { 177 178 System.out.println("文件大小ContentLength:" 179 + httpConnection.getContentLength()); 180 181 fileSize = Long.parseLong(httpConnection 182 .getHeaderField(sHeader)); 183 184 break; 185 186 } 187 188 } 189 190 return fileSize; 191 192 } 193 194 /** 195  *  196  * 创建指定大小的文件 197  */ 198 199 private void createFile(String fileName, long fileSize) throws IOException { 200 201 File newFile = new File(fileName); 202 203 RandomAccessFile raf = new RandomAccessFile(newFile, "rw"); 204 205 raf.setLength(fileSize); 206 207 raf.close(); 208 209 } 210 211 212 public TaskExecutor getTaskExecutor() { 213 return taskExecutor; 214 } 215 216 public void setTaskExecutor(TaskExecutor taskExecutor) { 217 this.taskExecutor = taskExecutor; 218 } 219 220}

DownloadThreadTest类:

1package xxxx; 2 3import java.io.BufferedInputStream; 4import java.io.File; 5import java.io.IOException; 6import java.io.RandomAccessFile; 7import java.util.concurrent.CountDownLatch; 8import org.apache.commons.lang.exception.ExceptionUtils; 9import org.apache.http.client.ClientProtocolException; 10import org.apache.http.client.methods.CloseableHttpResponse; 11import org.apache.http.client.methods.HttpGet; 12import org.apache.http.impl.client.CloseableHttpClient; 13import org.apache.http.protocol.BasicHttpContext; 14import org.apache.http.protocol.HttpContext; 15import org.slf4j.Logger; 16import org.slf4j.LoggerFactory; 17 18/** 19 *  20 * 负责文件下载的类 21 */ 22 23public class DownloadThreadTest extends Thread { 24 25 private static final Logger LOGGER = LoggerFactory 26 .getLogger(DownloadThreadTest.class); 27 28 /** 29  *  30  * 待下载的文件 31  */ 32 33 private String url = null; 34 35 /** 36  *  37  * 本地文件名 38  */ 39 40 private String fileName = null; 41 42 /** 43  *  44  * 偏移量 45  */ 46 47 private long offset = 0; 48 49 /** 50  *  51  * 分配给本线程的下载字节数 52  */ 53 54 private long length = 0; 55 56 private CountDownLatch end; 57 58 private CloseableHttpClient httpClient; 59 60 private HttpContext context; 61 62 /** 63  *  64  * @param url 65  *            下载文件地址 66  *  67  * @param fileName 68  *            另存文件名 69  *  70  * @param offset 71  *            本线程下载偏移量 72  *  73  * @param length 74  *            本线程下载长度 75  *  76  *  77  *  78  * @author Angus.wang 79  *  80  * */ 81 82 public DownloadThreadTest(String url, String file, long offset, long length, 83 CountDownLatch end, CloseableHttpClient httpClient) { 84 85 this.url = url; 86 87 this.fileName = file; 88 89 this.offset = offset; 90 91 this.length = length; 92 93 this.end = end; 94 95 this.httpClient = httpClient; 96 97 this.context = new BasicHttpContext(); 98 99 LOGGER.debug("偏移量=" + offset + ";字节数=" + length); 100 101 } 102 103 public void run() { 104 105 try { 106 107 HttpGet httpGet = new HttpGet(this.url); 108 httpGet.addHeader("Range", "bytes=" + this.offset + "-" 109 + (this.offset + this.length - 1)); 110 CloseableHttpResponse response = httpClient.execute(httpGet, 111 context); 112 ; 113 BufferedInputStream bis = new BufferedInputStream(response 114 .getEntity().getContent()); 115 116 byte[] buff = new byte[1024]; 117 118 int bytesRead; 119 120 File newFile = new File(fileName); 121 122 RandomAccessFile raf = new RandomAccessFile(newFile, "rw"); 123 124 while ((bytesRead = bis.read(buff, 0, buff.length)) != -1) { 125 raf.seek(this.offset); 126 raf.write(buff, 0, bytesRead); 127 this.offset = this.offset + bytesRead; 128 } 129 raf.close(); 130 bis.close(); 131 } catch (ClientProtocolException e) { 132 LOGGER.error("DownloadThread exception msg:{}",ExceptionUtils.getFullStackTrace(e)); 133 } catch (IOException e) { 134 LOGGER.error("DownloadThread exception msg:{}",ExceptionUtils.getFullStackTrace(e)); 135 } finally { 136 end.countDown(); 137 LOGGER.info(end.getCount() + " is go on!"); 138 System.out.println(end.getCount() + " is go on!"); 139 } 140 } 141 142}

application.xml

1<bean id="taskExecutor" 2 class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> 3 <!-- 线程池活跃的线程数 --> 4 <property name="corePoolSize" value="5" /> 5 <!-- 线程池最大活跃的线程数 --> 6 <property name="maxPoolSize" value="10" /> 7 <!-- 队列的最大容量 --> 8 <property name="queueCapacity" value="600" /> 9 </bean> 10 <bean id="downLoadManager" 11 class="xx.DownLoadManagerTest"> 12 <property name="taskExecutor" ref="taskExecutor" /> 13 </bean>

测试运行,500M,我这网速得半个小时左右。要想下载更大的文件,只要jdk内存够大,就无限更改队列最大容量吧。

如果不同意见,欢迎各位大神指正。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

FLV文件格式

1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.