這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)怎么在Java中利用多線程實(shí)現(xiàn)分片下載文件,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
在成都網(wǎng)站建設(shè)、成都網(wǎng)站制作過程中,需要針對客戶的行業(yè)特點(diǎn)、產(chǎn)品特性、目標(biāo)受眾和市場情況進(jìn)行定位分析,以確定網(wǎng)站的風(fēng)格、色彩、版式、交互等方面的設(shè)計方向。創(chuàng)新互聯(lián)建站還需要根據(jù)客戶的需求進(jìn)行功能模塊的開發(fā)和設(shè)計,包括內(nèi)容管理、前臺展示、用戶權(quán)限管理、數(shù)據(jù)統(tǒng)計和安全保護(hù)等功能。
1、如何請求才能拿到數(shù)據(jù)的特定部分,而非全部?
可以在HTTP請求頭中加入Range來標(biāo)識數(shù)據(jù)的請求范圍/區(qū)間,從HTTP/1.1開始可用。
基本用法:
Range: bytes=10-:取第10個字節(jié)及后所有數(shù)據(jù)。
Range: bytes=40-100:取第40個字節(jié)到第100個字節(jié)之間的數(shù)據(jù)。
這樣我們就能拿到特定部分的數(shù)據(jù)了,斷點(diǎn)續(xù)傳也可以用這個來實(shí)現(xiàn)。
PS:0為開始點(diǎn)。
2、分片后某線程下載時如何寫出?
思路1:等所有下載完成后進(jìn)行統(tǒng)一匯總整理然后再一次性寫出。
這簡直是最笨的思路了,如果文件過大全部拉到內(nèi)存中,豈不涼涼。
思路2:下載采用多線程,寫出時采取數(shù)據(jù)前后順序排隊(duì)寫出。
也就是說多線程下載,單線程輸出,某種程度解決了內(nèi)存占用問題,不過效率基本不理想。
思路3:要說還是API香,老大哥Java給我們提供了一個類叫做RandomAccessFile。
這個類可以進(jìn)行隨機(jī)文件讀寫,其中有一個seek函數(shù),可以將指針指向任意位置,然后進(jìn)行讀寫。什么意思呢,舉個栗子:假如我們開了30個線程,首先第一個下載完成的是線程X,它下載的數(shù)據(jù)范圍是4000-9000,那么這時我們調(diào)用seek函數(shù)將指針撥動到4000,然后調(diào)用它的write函數(shù)將byte寫出,這時4000之前都是NULL,4000之后就是我們插入的數(shù)據(jù)。這樣就可以實(shí)現(xiàn)多線程下載和本地寫入了。
具體實(shí)現(xiàn)
一個分片下載類,我們需要創(chuàng)建多個對象來進(jìn)行下載。
public class UnitDownloader implements Runnable { private int from; private int to; private File target; private String uri; private int id; public UnitDownloader(int from, int to, File target, String uri, int id) { this.from = from; this.to = to; this.target = target; this.uri = uri; this.id = id; } public int getFrom() { return from; } public int getTo() { return to; } @Override public void run() { //download and save data try { HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection(); connection.setRequestProperty("Range", "bytes=" + from + "-" + to); connection.connect(); int totalSize = connection.getContentLength(); InputStream inputStream = connection.getInputStream(); RandomAccessFile randomAccessFile = new RandomAccessFile(target, "rw"); randomAccessFile.seek(from); byte[] buffer = new byte[1024 * 1024]; int readCount = inputStream.read(buffer, 0, buffer.length); while (readCount > 0) { totalSize -= readCount; System.out.println("分片:" + this.id + "的剩余:" + totalSize); randomAccessFile.write(buffer, 0, readCount); readCount = inputStream.read(buffer, 0, buffer.length); } inputStream.close(); randomAccessFile.close(); } catch (IOException e) { e.printStackTrace(); } } }
分片下載管理器,主要就是拿到內(nèi)容的總大小,將其分配給每一個UnitDownloader。這里的threadCount函數(shù)可以再考慮優(yōu)化一下。
public class MultipleThreadDownloadManager implements Runnable { private String uri; private File target; public MultipleThreadDownloadManager(String uri, File target) { this.target = target; this.uri = uri; if (target.exists() == false) { try { target.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } } /** * 開始下載 */ public void start() { new Thread(this).start(); } /** * 根據(jù)文件總大小計算線程數(shù)量 * * @param totalSize * @return */ public int threadCount(int totalSize) { if (totalSize < 30 * 2014 * 1024) { return 1; } return 30; } @Override public void run() { //獲取文件總大小 int totalSize = 0; try { HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection(); connection.connect(); int contentLength = connection.getContentLength(); totalSize = contentLength; } catch (IOException e) { e.printStackTrace(); } //將文件分片并分開下載 int threadCount = threadCount(totalSize); int perThreadSize = totalSize / threadCount;//每一個線程分到的任務(wù)下載量 int id = 0; int from = 0, to = 0; while (totalSize > 0) { id++; //計算分片 if (totalSize < perThreadSize) { from = 0; to = totalSize; } else { from = totalSize; to = from + perThreadSize; } //開始下載 UnitDownloader downloader = new UnitDownloader(from, to, target, uri, id); new Thread(downloader).start(); } } }
上述就是小編為大家分享的怎么在Java中利用多線程實(shí)現(xiàn)分片下載文件了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。