程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 很簡略的Java斷點續傳完成道理

很簡略的Java斷點續傳完成道理

編輯:關於JAVA

很簡略的Java斷點續傳完成道理。本站提示廣大學習愛好者:(很簡略的Java斷點續傳完成道理)文章只能為提供參考,不一定能成為您想要的結果。以下是很簡略的Java斷點續傳完成道理正文


道理解析

在開辟傍邊,“斷點續傳”這類功效很適用和罕見,聽上去也是比擬有“逼格”的感到。所以平日我們都有興致去研討研討這類功效是若何完成的?
以Java來講,收集上也能找到很多關於完成相似功效的材料。然則呢,年夜多半都是舉個Demo然後貼出源碼,真正對其完成道理有具體的解釋很少。
因而我們在最後接觸的時刻,極可能就是直接Crtl + C/V代碼,然後搗鼓搗鼓,但是終究也能把後果弄出來。但初學時如許做其實很明顯是有好有壞的。
利益在於,源碼許多,說明很少;假如我們肯下工夫,針關於他人貼出的代碼裡那些本身不明確的器械去查材料,去研究。終究多半會收成頗豐。
害處也很顯著:作為初學者,面臨一年夜堆的源碼,感到很多多少器械都很生疏,就很輕易望而卻步。即便終究年夜致懂得了用法,但也紛歧定明確完成道理。

我們明天就一路從最根本的角度切入,來看看所謂的“斷點續傳”這個器械是否是真的如斯“高逼格”。
其其實接觸一件新的“事物”的時刻,將它擬化成一些我們自己比擬熟習的事物,來參照和比較著進修。平日會事半功倍。
假如我們剛接觸“斷點續傳”這個概念,確定很難說清晰個一二三。那末,“玩游戲”我們確定不會生疏。

OK,那就假定我們如今有一款“通關制的RPG游戲”。想一想我們在玩這類游戲時平日會怎樣做?
很顯著,第一天我們浴血奮戰,年夜殺四方,假定終究離開了第四關。固然鏖戰正酣,但一看牆上的時鐘,曾經清晨12點,該睡覺了。
這個時刻就很為難了,為了可以或許鄙人一次玩的時刻,順遂接軌上我們本次游戲的進度,我們應當怎樣辦呢?
很簡略,我們不關失落游戲,直接去睡覺,第二天再接著玩呗。如許是可以,但仿佛總覺著有哪裡讓人不爽。
那末,這個時刻,假如這個游戲有一個功效叫做“存檔”,就很症結了。我們直接選擇存檔,輸出存檔名“第四關”,然後便可以封閉游戲了。
比及下次停止游戲時,我們直接找到“第四關”這個存檔,然落後行讀檔,便可以接著停止游戲了。

這個時刻,所謂的“斷點續傳”就很好懂得了。我們順著我們之前“玩游戲”的思緒來理一下:
假定,如今有一個文件須要我們停止下載,當我們下載了一部門的時刻,湧現情形了,好比:電腦逝世機、沒電、收集中止等等。
其實這就比如我們之前玩游戲玩著玩著,忽然12點須要去睡覺歇息了是一個事理。OK,那末這個時刻的情形是:

 • 假如游戲不克不及存檔,那末則意味著我們下次游戲的時刻,此次曾經經由過程的4關的進度將會喪失,沒法接檔。
 • 對應的,假如“下載”的行動沒法記載本次下載的一個進度。那末,當我們再次下載這個文件也就只能從頭來過。
 話到這裡,其實我們曾經發明了,關於我們以上所說的行動,症結就在於一個字“續”!
而我們要完成讓一種斷開的行動“續”起來的目標,症結就在於要有“介質”可以或許記載和讀取行動湧現”中止”的這個節點的信息。

轉化到編程世界

現實上這就是“斷點續傳”最最基本的道理,用年夜白話說就是:我們要鄙人載行動湧現中止的時刻,記載下中止的地位信息,然後鄙人次行動中讀取。
有了這個地位信息以後,想一想我們該怎樣做。是的,很簡略,在新的下載行動開端的時刻,直接從記載的這個地位開端下載內容,而不再從頭開端。
好吧,我們用年夜白話掰扯了這麼久的道理,開端認為無聊了。那末我們如今最初總結一下,然後就來看看我們應當怎樣把道理轉換到編程世界中去。

 • 當“上傳(下載)的行動”湧現中止,我們須要記載本次上傳(下載)的地位(position)。
 • 當“續”這一行動開端,我們直接跳轉到postion處持續上傳(下載)的行動。 

明顯成績的症結就在於所謂的“position”,以我們舉的“通關游戲來講”,可以用“第幾關”來作為這個position的單元。
那末轉換到所謂的“斷點續傳”,我們該應用甚麼來權衡“position”呢?很明顯,回歸二進制,由於這裡的實質不過就是文件的讀寫。

那末剩下的任務就很簡略了,先是記載position,這仿佛都沒甚麼值得說的,由於只是數據的耐久化罷了(內存,文件,數據庫),我們有許多方法。

另外一個症結在於當“續傳”的行動開端,我們須要須要從前次記載的position地位開端讀寫操作,所以我們須要一個相似於“指針”功效的器械。
我們固然也能夠本身想方法去完成如許一個“指針”,但愉快地是,Java曾經為我們供給了如許的一個類,那就是RandomAccessFile。
這個類的功效從名字就很直不雅的表現了,可以或許隨機的去拜訪文件。我們看一下API文檔中對該類的解釋:

此類的實例支撐對隨機拜訪文件的讀取和寫入。隨機拜訪文件的行動相似存儲在文件體系中的一個年夜型 byte 數組。

假如隨機拜訪文件以讀取/寫入形式創立,則輸入操作也可用;輸入操作從文件指針開端寫入字節,並跟著對字節的寫入而前移此文件指針。

寫入隱含數組確當前末尾以後的輸入操作招致該數組擴大。該文件指針可以經由過程 getFilePointer 辦法讀取,並經由過程 seek 辦法設置。 

看完API解釋,我們笑了,是的,這不恰是我們要的嗎?那好吧,我們磨刀磨了這麼久了,還不去砍砍柴嗎?

實例演示

既然是針關於文件的“斷點續傳”,那末很顯著,我們先弄一個文件出來。或許音頻文件,圖象文件甚麼的看上去會更上層次一點。
但我們曾經說了,在盤算機年夜兄弟眼中,它們終究都將回歸“二進制”。所以我們這裡就創立一個簡略的”txt”文件,由於txt更利於懂得。

我們在D盤的根目次下創立一個名為”test.txt”的文件,文件內容很簡略,如圖所示:


沒錯,我們輸出的內容就是簡略的6個英語字母。然後我們右鍵→屬性:


我們看到,文件如今的年夜小是6個字節。這也就是為何我們說,一切的器械到最初照樣離不開“二進制”。
是的,我們都明確,由於我們輸出了6個英文字母,而1個英文字母將占領的存儲空間是1個字節(即8個比特位)。
今朝為止,我們看到的都很無聊,由於這根本等於空話,略微有盤算機知識的人都曉得這些常識。別焦急,我們持續。

在Java中對一個文件停止讀寫操作很簡略。假定如今的需求假如是“把D盤的這個文件寫入到E盤”,那末我們會提起鍵盤,啪啪啪啪,弄定!
但其實所謂的文件的“上傳(下載)”不是也沒甚麼分歧嗎?差別就僅僅在於行動由“僅僅在本機之間”改變成了”本機與辦事器之間”的文件讀寫。
這時候我們會說,“別逼逼了,這些誰都曉得,‘斷點續傳'呢?“,其實到了這裡也曾經很簡略了,我們再次明白,斷點續傳要做的不過就是:
前一次讀寫行動假如湧現中止,請記載下此次讀寫完成的文件內容的地位信息;當“續傳開端”則直接將指針移到此處,開端持續讀寫操作。

重復的強調道理,現實上是由於只需弄明確了道理,剩下的就只是招式罷了了。這就就像武俠小說裡的“九九歸一”年夜法一樣,最高境地就是回歸根源。
任何龐雜的事物,只需明確其道理,我們就可以將其剝離,復原為一個個簡略的事物。同理,一系列簡略的事物,經由邏輯組合,就構成了龐雜的事物。

上面,我們立時就將回歸渾沌,以最根本的情勢模仿一次“斷點續傳”。在這裡我們連辦事器的代碼都不去寫了,直接經由過程一個當地測試類弄定。
我們要完成的後果很簡略:將在D盤的”test.txt”文件寫入到E盤傍邊,但半途我們會模仿一次”中止”行動,然後在從新持續上傳,終究完成全部進程。
也就是說,我們這裡將會把“D盤”視作一台電腦,而且直接將”E盤”視作一台辦事器。那末如許我們乃至都不再與http協定扯上半毛錢關系了,(固然現實開辟我們確定是照樣得與它扯上關系的 ^<^),從而只關懷最根本的文件讀寫的”斷”和”續”的道理是怎樣樣的。

為了經由過程比較加深懂得,我們先來寫一段正常的代碼,即正常讀寫,不產生中止:

public class Test {

 public static void main(String[] args) {
  // 源文件與目的文件
  File sourceFile = new File("D:/", "test.txt");
  File targetFile = new File("E:/", "test.txt");
  // 輸出輸入流
  FileInputStream fis = null;
  FileOutputStream fos = null;
  // 數據緩沖區
  byte[] buf = new byte[1];

  try {
   fis = new FileInputStream(sourceFile);
   fos = new FileOutputStream(targetFile);
   // 數據讀寫
   while (fis.read(buf) != -1) {
    System.out.println("write data...");
    fos.write(buf);
   }
  } catch (FileNotFoundException e) {
   System.out.println("指定文件不存在");
  } catch (IOException e) {
   // TODO: handle exception
  } finally {
   try {
    // 封閉輸出輸入流
    if (fis != null)
     fis.close();

    if (fos != null)
     fos.close();
   } catch (IOException e) {
    e.printStackTrace();
   }

  }
 }
}

該段代碼運轉,我們就會發明在E盤中曾經勝利拷貝了一份“test.txt”。這段代碼很簡略,獨一略微說一下就是:
我們看到我們將buf,即緩沖區 設置的年夜小是1,這其實就代表我們每次read,是讀取一個字節的數據(即1個英文字母)。

如今,我們就來模仿這個讀寫中止的行動,我們將之前的代碼完美以下:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;

public class Test {

 private static int position = -1;

 public static void main(String[] args) {
  // 源文件與目的文件
  File sourceFile = new File("D:/", "test.txt");
  File targetFile = new File("E:/", "test.txt");
  // 輸出輸入流
  FileInputStream fis = null;
  FileOutputStream fos = null;
  // 數據緩沖區
  byte[] buf = new byte[1];

  try {
   fis = new FileInputStream(sourceFile);
   fos = new FileOutputStream(targetFile);
   // 數據讀寫
   while (fis.read(buf) != -1) {
    fos.write(buf);
    // 當曾經上傳了3字節的文件內容時,收集中止了,拋出異常
    if (targetFile.length() == 3) {
     position = 3;
     throw new FileAccessException();
    }
   }
  } catch (FileAccessException e) {
   keepGoing(sourceFile,targetFile, position);
  } catch (FileNotFoundException e) {
   System.out.println("指定文件不存在");
  } catch (IOException e) {
   // TODO: handle exception
  } finally {
   try {
    // 封閉輸出輸入流
    if (fis != null)
     fis.close();

    if (fos != null)
     fos.close();
   } catch (IOException e) {
    e.printStackTrace();
   }

  }
 }

 private static void keepGoing(File source,File target, int position) {
  try {
   Thread.sleep(10000);
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

  try {
   RandomAccessFile readFile = new RandomAccessFile(source, "rw");
   RandomAccessFile writeFile = new RandomAccessFile(target, "rw");
   readFile.seek(position);
   writeFile.seek(position);

   // 數據緩沖區
   byte[] buf = new byte[1];
   // 數據讀寫
   while (readFile.read(buf) != -1) {
    writeFile.write(buf);
   }
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

}

class FileAccessException extends Exception {

}

總結一下,我們在此次修改傍邊都做了甚麼任務:

 • 起首,我們界說了一個變量position,記載在產生中止的時刻,已完成讀寫的地位。(這是為了便利,現實來講確定應當講這個值存到文件或許數據庫等停止耐久化)
 • 然後在文件讀寫的while輪回中,我們去模仿一個中止行動的產生。這裡是當targetFile的文件長度為3個字節則模仿拋出一個我們自界說的異常。(我們可以想象為現實下載中,曾經上傳(下載)了”x”個字節的內容,這個時刻收集中止了,那末我們就在收集中止拋出的異常中將”x”記載上去)。
 • 剩下的就假如我們之前說的一樣,在“續傳”行動開端後,經由過程RandomAccessFile類來包裝我們的文件,然後經由過程seek將指針指定到之前產生中止的地位停止讀寫就弄定了。
(現實的文件下載上傳,我們固然須要將保留的中止值上傳給辦事器,這個方法平日為httpConnection.setRequestProperty(“RANGE”,”bytes=x”);)

在我們這段代碼,開啟”續傳“行動,即keepGoing辦法中:我們開端讓線程休眠10秒鐘,這恰是為了讓我們運轉法式看到後果。
如今我們運轉法式,那末文件就會開啟“由D盤上傳到E盤的進程”,我們起首點開E盤,會發明切實其實多了一個test.txt文件,翻開它發明內容以下:


沒錯,這個時刻我們發明內容只要“abc”。這是在我們預感之內的,由於我們的法式模仿在文件上傳了3個字節的時刻產生了中止。

Ok,我們靜靜的期待10秒鐘曩昔,然後再點開該文件,看看能否可以或許勝利:

經由過程截圖我們發明內容切實其實曾經釀成了“abc”,由此也就完成了續傳。

以上就是本文的全體內容,願望對年夜家的進修有所贊助,也願望年夜家多多支撐。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved