Using Java to achieve HTTP multithreading breakpoints download file of two

  • 2020-04-01 01:24:51
  • OfStack

Download tools I think few people won't use it, some time ago is boring, spend a little bit of time using Java to write a simple HTTP multi-threaded download procedures, a pure is boring to write, only a few simple functions, but also didn't write interface, today is a boring day, is to write an article, to swim, feel good to give a applause, is bad also don't spray, thank you!
The HTTP download tool I implemented was very simple, a multi-threaded and breakpoint recovery, and of course the download was essential. then Sort out your to-do list first :
1. Connect to the resource server, obtain resource information and create files
2, shard resources, multi-threaded download
3. Recovery function of breakpoint
4. Download rate statistics
About these points, then the first thing to do is to connect resources and get resource information, I use the JavaSE's own URLConnection for resource connection, the general code is as follows:

 
String urlStr =  " http://www.sourcelink.com/download/xxx " ; //Resource address, whatever
URL url = new URL(urlStr); //Create a URL
URLConnection con = url.openConnection(); //Establish a connection
contentLen = con.getContentLength(); //Get resource length
File file = new File(filename); //Creating a download file based on filename will also be the file we end up downloading

That's easy, that's it, that's it, so we're done with the first step, and then we're going to do the second step, which is to shred resources and implement multithreading. In the previous step we already had the length of the resource contentLen, so how do we slice the resource based on that? If we were to run ten threads, then we will put contentLen sentenced to 10, get the size of each, and then create ten threads in respectively, each thread is responsible for one piece of writing, which requires using the RandomAccessFile this class, this class provides random access to the file, you can specify the file in a position to write operation, roughly as follows:
 
long subLen = contentLen / threadQut; //Gets the size of each block
//Create ten threads and start the thread
for (int i = 0; i < threadQut; i++) { 
DLThread thread = new DLThread(this, i + 1, subLen * i, subLen * (i + 1) - 1); //Create a thread
dlThreads[i] = thread; 
QSEngine.pool.execute(dlThreads[i]); //Turn threads over to the thread pool for management
} 

Using the class DLThread here, let's first look at the definition of the constructor for this class:
Public DLThread(DLTask DLTask, int id, long startPos, long endPos)
The first parameter is a DLTask, this class represents a download task, which mainly stores the information of this download task, including the download resource name, local file name and so on. The second parameter is an id that identifies the thread. If there are 10 threads, the id is from 1 to 10. The third parameter, startPos, indicates where the thread starts writing from in the file, and the last parameter, endPos, indicates where it ends.
Let's take a look at how to download after a thread starts. See the run method:
 
public void run() { 
System.out.println(" thread " + id + " Start the "); 
BufferedInputStream bis = null; //Create a buff
RandomAccessFile fos = null; 
byte[] buf = new byte[BUFFER_SIZE]; //Buffer size
URLConnection con = null; 
try { 
con = url.openConnection(); //Create a connection, which creates a connection for each thread
con.setAllowUserInteraction(true); 
if (isNewThread) { 
con.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);//Set the scope to get the resource data, from startPos to endPos
fos = new RandomAccessFile(file, "rw"); //Create a RandomAccessFile
fos.seek(startPos); //Since startPos
} else { 
con.setRequestProperty("Range", "bytes=" + curPos + "-" + endPos); 
fos = new RandomAccessFile(dlTask.getFile(), "rw"); 
fos.seek(curPos); 
} 
//The following section writes data to the file, curPos is the unknown currently written, here it will determine whether it is less than endPos,
//If the endPos is exceeded, the thread has completed execution
bis = new BufferedInputStream(con.getInputStream()); 
while (curPos < endPos) { 
int len = bis.read(buf, 0, BUFFER_SIZE); 
if (len == -1) { 
break; 
} 
fos.write(buf, 0, len); 
curPos = curPos + len; 
if (curPos > endPos) { 
readByte += len - (curPos - endPos) + 1; //Gets the number of bytes read correctly
} else { 
readByte += len; 
} 
} 
System.out.println(" thread " + id + " It has been downloaded. "); 
this.finished = true; 
bis.close(); 
fos.close(); 
} catch (IOException ex) { 
ex.printStackTrace(); 
throw new RuntimeException(ex); 
} 
} 

The above code is based on the operation of startPos and endPos to the file model, each thread has its own independent resource block, from startPos to endPos. Is the core of thread to download the above way, multi-threaded done, the next is to implement breakpoints recovery function, breakpoint recovery does not actually recorded each thread to which the unknown, here I am using curPos record, which you can see in the above code should be, I will keep a record of each thread curPos, then resumed in the thread, the curPos as startPos, while endPost remains the same, you have not noticed in the run method has a code like this:
 
if (isNewThread) { //Determines whether a breakpoint, if true, is a new download thread, not a breakpoint recovery
con.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);//Set the scope to get the resource data, from startPos to endPos
fos = new RandomAccessFile(file, "rw"); //Create a RandomAccessFile
fos.seek(startPos); //Since startPos
} else { 
con.setRequestProperty("Range", "bytes=" + curPos + "-" + endPos);//Using curPos instead of startPos, everything else is the same as creating a new one.
fos = new RandomAccessFile(dlTask.getFile(), "rw"); 
fos.seek(curPos); 
} 

So that's what breakpoint recovery does, it's not different than creating a new thread, it's just startPos, it's all the same, but that's not enough, because if the program is closed, how do you save that information? Such as file names, each thread curPos, etc., all at the time of use the download software, believe you will find in the software didn't finish my download, can have two temporary files in the directory, and one of them is used to save the download task information, without these information, is don't know how to restore the download progress. And how do I do that? I this person is lazy, and don't want to create a file to store information, and then you are to read the information to create objects, that is too much trouble, so I thought about the Java serialization mechanism, my idea is to direct the whole DLTask object serialized to disk, the above said DLTask this class is used to store information of each task, so I just in need of recovery and deserialize the object, you can easily realize the function of breakpoint, let's take a look at this object stored information:
 
public class DLTask extends Thread implements Serializable { 
private static final long serialVersionUID = 126148287461276024L; 
private final static int MAX_DLTHREAD_QUT = 10; //Maximum number of download threads
 
public final static String FILE_POSTFIX = ".tmp"; 
private URL url; 
private File file; 
private String filename; 
private int id; 
private int Level; 
private int threadQut; //Number of download threads, user can customize
private int contentLen; //Download file length
private long completedTot; //Total number of downloads completed
private int costTime; //Download time count, record the download time
private String curPercent; //Download percentage
private boolean isNewTask; //Whether to create a new download task may be a breakpoint continuation task
private DLThread[] dlThreads; //Saves the thread of the current task
transient private DLListener listener; //The listener for the current task to get the relevant download information immediately

Above code, the object implements the Serializable interface, save all the information of the task, also includes dlThreads each thread object, like this can easily be breakpoint back, let me write a file to save this information, and then at the time of recovery according to these information to create an object, it is to my life. Here a method is created for breakpoint recovery:
 
private void resumeTask() { 
listener = new DLListener(this); 
file = new File(filename); 
for (int i = 0; i < threadQut; i++) { 
dlThreads[i].setDlTask(this); 
QSEngine.pool.execute(dlThreads[i]); 
} 
QSEngine.pool.execute(listener); 
} 

In effect, it reduces the amount of code that connects resources and then shred them, because the information is already stored under the DLTask object.
See the code above, don't know everyone noticed DLListener didn't have a object, the object is actually used to monitor the entire task information, here I mainly used for two purposes, one is the timing to serialize DLTask, save the task information, used for breakpoints recovery, one is to download rate, how long does it take for a statistical average. Let's look at its code first. This class is also a separate thread:
 
public void run() { 
int i = 0; 
BigDecimal completeTot = null; //Percentage completed
long start = System.currentTimeMillis(); //The current time used to record the start time of the statistics
long end = start; 
while (!dlTask.isComplete()) { //If the whole task is completed or not, the loop continues
i++; 
String percent = dlTask.getCurPercent(); //Gets the current completion percentage
completeTot = new BigDecimal(dlTask.getCompletedTot()); //Gets the total number of bytes currently completed
//Get the current time, then compare it to the start time, and if not, take the total number of current completions divided by the time spent to get an average download speed
end = System.currentTimeMillis(); 
if (end - start != 0) { 
BigDecimal pos = new BigDecimal(((end - start) / 1000) * 1024); 
System.out.println("Speed :" 
+ completeTot 
.divide(pos, 0, BigDecimal.ROUND_HALF_EVEN) 
+ "k/s " + percent + "% completed. "); 
} 
recoder.record(); //Record task information to hard drive
try { 
sleep(3000); 
} catch (InterruptedException ex) { 
ex.printStackTrace(); 
throw new RuntimeException(ex); 
} 
} 
//The following is the information for printing the entire download task after the download is completed
int costTime =+ (int)((System.currentTimeMillis() - start) / 1000); 
dlTask.setCostTime(costTime); 
String time = QSDownUtils.changeSecToHMS(costTime); 
dlTask.getFile().renameTo(new File(dlTask.getFilename())); 
System.out.println("Download finished. " + time); 
} 

Recoder.record () method in this method is used to serialize the task object, other code are used for statistical information, specific can see the comments, record method code as follows:
 
public void record() { 
ObjectOutputStream out = null; 
try { 
out = new ObjectOutputStream(new FileOutputStream(dlTask.getFilename() + ".tsk")); 
out.writeObject(dlTask); 
out.close(); 
} catch (IOException ex) { 
ex.printStackTrace(); 
throw new RuntimeException(ex); 
} finally { 
try { 
out.close(); 
} catch (IOException ex) { 
ex.printStackTrace(); 
throw new RuntimeException(ex); 
} 
} 
} 

Here, roughly the code is done, but the above code is part of the segment, just as a reference for everyone to see, and because my level is limited, in many parts of the code without too much consideration, without optimization, just for fun, so there may be a lot of places all write very bad, this program also lack a lot of features, even without interface, so the program's code is not upload, lest a disgrace, ha ha. I hope I can be of some help to those who are interested.


Related articles: