(1) Das Prinzip des fortsetzbaren Downloads <BR>Tatsächlich ist das Prinzip des fortsetzbaren Downloads sehr einfach. Die HTTP-Anfrage unterscheidet sich lediglich vom allgemeinen Download.
Wenn ein Browser beispielsweise eine Datei auf dem Server anfordert, lautet die Anfrage wie folgt:
Gehen Sie davon aus, dass der Serverdomänenname wwww.sjtu.edu.cn und der Dateiname down.zip lautet.
GET /down.zip HTTP/1.1
Akzeptieren: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-
Excel, application/msword, application/vnd.ms-powerpoint, */*
Akzeptierte Sprache: zh-cn
Akzeptierte Kodierung: gzip, deflate
Benutzeragent: Mozilla/4.0 (kompatibel; MSIE 5.01; Windows NT 5.0)
Verbindung: Keep-Alive
Nachdem der Server die Anforderung erhalten hat, sucht er nach Bedarf nach der angeforderten Datei, extrahiert die Dateiinformationen und gibt sie dann an den Browser zurück. Die zurückgegebenen Informationen lauten wie folgt
200
Inhaltslänge=106786028
Accept-Ranges=Bytes
Datum=Mo, 30. April 2001 12:56:11 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=Anwendung/Oktett-Stream
Server=Microsoft-IIS/5.0
Zuletzt geändert=Mo, 30. April 2001 12:56:11 GMT
Der sogenannte Resume-Download bedeutet, den Download an der Stelle fortzusetzen, an der die Datei heruntergeladen wurde. Also im Client-Browser passieren
Wenn es um Webserver geht, muss noch eine weitere Information hinzugefügt werden – wo man anfangen soll.
Das Folgende ist ein von mir zusammengestellter „Browser“, um die Anforderungsinformationen an den Webserver zu übergeben. Die Anforderung beginnt bei 2000070 Bytes.
GET /down.zip HTTP/1.0
Benutzeragent: NetFox
BEREICH: Bytes=2000070-
Akzeptieren: text/html, image/gif, image/jpeg, *; q=.2, */*;
Wenn Sie genau hinschauen, werden Sie eine zusätzliche Zeile von RANGE finden: bytes=2000070-
Die Bedeutung dieser Zeile besteht darin, dem Server mitzuteilen, dass die Datei down.zip ab 2000070 Bytes übertragen wird und die vorherigen Bytes nicht übertragen werden müssen.
Nachdem der Server diese Anfrage erhalten hat, lauten die zurückgegebenen Informationen wie folgt:
206
Inhaltslänge=106786028
Content-Range=Bytes 2000070-106786027/106786028
Datum=Mo, 30. April 2001 12:55:20 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=Anwendung/Oktett-Stream
Server=Microsoft-IIS/5.0
Zuletzt geändert=Mo, 30. April 2001 12:55:20 GMT
Wenn Sie es mit den vom vorherigen Server zurückgegebenen Informationen vergleichen, werden Sie feststellen, dass eine Zeile hinzugefügt wurde:
Content-Range=Bytes 2000070-106786027/106786028
Der Rückkehrcode wurde ebenfalls auf 206 statt 200 geändert.
Wenn Sie die oben genannten Prinzipien kennen, können Sie das Herunterladen von Haltepunkten programmieren.
(2) Wichtige Punkte bei der Implementierung der Breakpoint-Resume-Übertragung in Java
(1) Welche Methode wird zum Senden von RANGE verwendet: Bytes = 2000070-.
Natürlich ist dies durchaus mit dem Original-Socket möglich, aber das wäre zu umständlich. Tatsächlich bietet das Net-Paket von Java diese Funktion. Der Code lautet wie folgt:
URL url = neue URL("http://www.sjtu.edu.cn/down.zip");
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();
//Benutzeragenten festlegen
httpConnection.setRequestProperty("User-Agent","NetFox");
//Legen Sie die Startposition der Haltepunkt-Wiederaufnahmeübertragung fest
httpConnection.setRequestProperty("RANGE","bytes=2000070");
//Eingabestream abrufen
InputStream input = httpConnection.getInputStream();
Der aus dem Eingabestream entnommene Bytestream ist der Bytestream, der bei 2000070 in der Datei down.zip beginnt.
Sie sehen, es ist tatsächlich sehr einfach, die Übertragung von Haltepunktfortsetzungen in Java zu implementieren.
Als nächstes müssen Sie den erhaltenen Stream in einer Datei speichern.
Die zum Speichern der Datei verwendete Methode.
Ich verwende die RandAccessFile-Klasse im IO-Paket.
Der Vorgang ist recht einfach. Gehen Sie davon aus, dass die Datei ab 2000070 gespeichert wird. Der Code lautet wie folgt:
Kopieren Sie den Codecode wie folgt:
RandomAccess oSavedFile = new RandomAccessFile("down.zip","rw");
long nPos = 2000070;
//Suchen Sie den Dateizeiger auf die nPos-Position
oSavedFile.seek(nPos);
byte[] b = neues Byte[1024];
int nRead;
//Lesen Sie den Bytestream aus dem Eingabestream und schreiben Sie ihn in die Datei
while((nRead=input.read(b,0,1024)) > 0)
{
oSavedFile.write(b,0,nRead);
}
Wie auch immer, es ist sehr einfach.
Als nächstes gilt es, es in ein Gesamtprogramm zu integrieren. Einschließlich einer Reihe von Thread-Kontrollen und so weiter.
(3) Die Implementierung des Breakpoint-Resume-Kernels <BR>verwendet hauptsächlich 6 Klassen, einschließlich einer Testklasse.
SiteFileFetch.java ist dafür verantwortlich, die gesamte Datei abzurufen und den internen Thread zu steuern (FileSplitterFetch-Klasse).
FileSplitterFetch.java ist für das Abrufen einiger Dateien verantwortlich.
FileAccess.java ist für die Dateispeicherung verantwortlich.
Informationen über die Datei, die SiteInfoBean.java crawlen möchte, z. B. das Verzeichnis, in dem die Datei gespeichert ist, ihr Name, die URL der gecrawlten Datei usw.
Setzen Sie in der Utility.java-Toolklasse einige einfache Methoden ein.
TestMethod.java-Testklasse.
Das Folgende ist das Quellprogramm:
Kopieren Sie den Codecode wie folgt:
/*
**SiteFileFetch.java
*/
Paket NetFox;
java.io.* importieren;
java.net importieren.*;
Die öffentliche Klasse SiteFileFetch erweitert Thread {
SiteInfoBean siteInfoBean = null; //Dateiinformations-Bean
long[] nStartPos; //Startposition
long[] nEndPos; //Endposition
FileSplitterFetch[] fileSplitterFetch; //Sub-Thread-Objekt
long nFileLength; //Dateilänge
boolean bFirst = true; //Ob die Datei zum ersten Mal abgerufen werden soll
boolean bStop = false; //Stoppzeichen
File tmpFile; //Temporäre Informationen zum Herunterladen der Datei
DataOutputStream-Ausgabe; //Ausgabestream in Datei
public SiteFileFetch(SiteInfoBean bean) löst eine IOException aus
{
siteInfoBean = bean;
//tmpFile = File.createTempFile ("zhong","1111",new File(bean.getSFilePath()));
tmpFile = new File(bean.getSFilePath()+File.separator + bean.getSFileName()+".info");
if(tmpFile.exists ())
{
bFirst = false;
read_nPos();
}
anders
{
nStartPos = new long[bean.getNSplitter()];
nEndPos = new long[bean.getNSplitter()];
}
}
public void run()
{
//Ermitteln Sie die Dateilänge
//Datei teilen
//Instanz FileSplitterFetch
//Starte den FileSplitterFetch-Thread
//Warten Sie, bis der untergeordnete Thread zurückkehrt
versuchen{
if(bFirst)
{
nFileLength = getFileSize();
if(nFileLength == -1)
{
System.err.println("Dateilänge ist nicht bekannt!");
}
sonst if(nFileLength == -2)
{
System.err.println("Auf die Datei kann nicht zugegriffen werden!");
}
anders
{
for(int i=0;i<nStartPos.length;i++)
{
nStartPos[i] = (long)(i*(nFileLength/nStartPos.length));
}
for(int i=0;i<nEndPos.length-1;i++)
{
nEndPos[i] = nStartPos[i+1];
}
nEndPos[nEndPos.length-1] = nFileLength;
}
}
//Untergeordneten Thread starten
fileSplitterFetch = new FileSplitterFetch[nStartPos.length];
for(int i=0;i<nStartPos.length;i++)
{
fileSplitterFetch[i] = new FileSplitterFetch(siteInfoBean.getSSiteURL(),
siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),
nStartPos[i],nEndPos[i],i);
Utility.log("Thread " + i + " , nStartPos = " + nStartPos[i] + ", nEndPos = " + nEndPos[i]);
fileSplitterFetch[i].start();
}
// fileSplitterFetch[nPos.length-1] = new FileSplitterFetch(siteInfoBean.getSSiteURL(),
siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),nPos[nPos.length-1],nFileLength,nPos.length-1);
// Utility.log("Thread " + (nPos.length-1) + " , nStartPos = " + nPos[nPos.length-1] + ",
nEndPos = " + nFileLength);
// fileSplitterFetch[nPos.length-1].start();
//Warten Sie, bis der untergeordnete Thread beendet ist
//int count = 0;
//Ob die While-Schleife beendet werden soll
boolean breakWhile = false;
while(!bStop)
{
write_nPos();
Utility.sleep(500);
breakWhile = true;
for(int i=0;i<nStartPos.length;i++)
{
if(!fileSplitterFetch[i].bDownOver)
{
breakWhile = false;
brechen;
}
}
if(breakWhile)
brechen;
//count++;
//if(count>4)
// siteStop();
}
System.err.println("Dateidownload abgeschlossen!");
}
Catch(Exception e){e.printStackTrace ();}
}
//Ermitteln Sie die Dateilänge
public long getFileSize()
{
int nFileLength = -1;
versuchen{
URL url = neue URL(siteInfoBean.getSSiteURL());
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();
httpConnection.setRequestProperty("User-Agent","NetFox");
int ResponseCode=httpConnection.getResponseCode();
if(responseCode>=400)
{
processErrorCode(responseCode);
return -2; //-2 bedeutet, dass der Zugriff fehlerhaft ist
}
String sHeader;
for(int i=1;;i++)
{
//DataInputStream in = new DataInputStream(httpConnection.getInputStream ());
//Utility.log(in.readLine());
sHeader=httpConnection.getHeaderFieldKey(i);
if(sHeader!=null)
{
if(sHeader.equals("Content-Length"))
{
nFileLength = Integer.parseInt(httpConnection.getHeaderField(sHeader));
brechen;
}
}
anders
brechen;
}
}
Catch(IOException e){e.printStackTrace ();}
Catch(Exception e){e.printStackTrace ();}
Utility.log(nFileLength);
return nFileLength;
}
//Download-Informationen speichern (Dateizeigerposition)
private void write_nPos()
{
versuchen{
Ausgabe = neuer DataOutputStream(neuer FileOutputStream(tmpFile));
Output.writeInt(nStartPos.length);
for(int i=0;i<nStartPos.length;i++)
{
// Ausgabe.writeLong(nPos[i]);
Output.writeLong(fileSplitterFetch[i].nStartPos);
Output.writeLong(fileSplitterFetch[i].nEndPos);
}
Ausgabe.close();
}
Catch(IOException e){e.printStackTrace ();}
Catch(Exception e){e.printStackTrace ();}
}
//Lesen Sie die gespeicherten Download-Informationen (Dateizeigerposition)
private void read_nPos()
{
versuchen{
DataInputStream input = new DataInputStream(new FileInputStream(tmpFile));
int nCount = input.readInt();
nStartPos = new long[nCount];
nEndPos = new long[nCount];
for(int i=0;i<nStartPos.length;i++)
{
nStartPos[i] = input.readLong();
nEndPos[i] = input.readLong();
}
input.close();
}
Catch(IOException e){e.printStackTrace ();}
Catch(Exception e){e.printStackTrace ();}
}
private void ProcessErrorCode(int nErrorCode)
{
System.err.println("Fehlercode: " + nErrorCode);
}
//Datei-Download stoppen
public void siteStop()
{
bStop = true;
for(int i=0;i<nStartPos.length;i++)
fileSplitterFetch[i].splitterStop();
}
}
/*
**FileSplitterFetch.java
*/
Paket NetFox;
java.io.* importieren;
java.net importieren.*;
öffentliche Klasse FileSplitterFetch erweitert Thread {
String sURL; //Datei-URL
long nStartPos; //Startposition des Dateiausschnitts
long nEndPos; //Endposition des Dateiausschnitts
int nThreadID; //Thread-ID
boolean bDownOver = false; //Downing ist vorbei
boolean bStop = false; //Stopp identisch
FileAccessI fileAccessI = null; //Dateizugriffsschnittstelle
public FileSplitterFetch(String sURL,String sName,long nStart,long nEnd,int id) löst eine IOException aus
{
this.sURL = sURL;
this.nStartPos = nStart;
this.nEndPos = nEnd;
nThreadID = id;
fileAccessI = new FileAccessI(sName,nStartPos);
}
public void run()
{
while(nStartPos < nEndPos && !bStop)
{
versuchen{
URL url = neue URL(sURL);
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();
httpConnection.setRequestProperty("User-Agent","NetFox");
String sProperty = "bytes="+nStartPos+"-";
httpConnection.setRequestProperty("RANGE",sProperty);
Utility.log(sProperty);
InputStream input = httpConnection.getInputStream();
//logResponseHead(httpConnection);
byte[] b = neues Byte[1024];
int nRead;
while((nRead=input.read(b,0,1024)) > 0 && nStartPos < nEndPos && !bStop)
{
nStartPos += fileAccessI.write(b,0,nRead);
//if(nThreadID == 1)
// Utility.log("nStartPos = " + nStartPos + ", nEndPos = " + nEndPos);
}
Utility.log("Thread " + nThreadID + " ist vorbei!");
bDownOver = true;
//nPos = fileAccessI.write (b,0,nRead);
}
Catch(Exception e){e.printStackTrace ();}
}
}
//Antwort-Header-Informationen drucken
public void logResponseHead(HttpURLConnection con)
{
for(int i=1;;i++)
{
String header=con.getHeaderFieldKey(i);
if(header!=null)
//responseHeaders.put(header,httpConnection.getHeaderField(header));
Utility.log(header+" : "+con.getHeaderField(header));
anders
brechen;
}
}
public void splitterStop()
{
bStop = true;
}
}
/*
**FileAccess.java
*/
Paket NetFox;
java.io.* importieren;
Die öffentliche Klasse FileAccessI implementiert Serializable{
RandomAccessFile oSavedFile;
lange nPos;
public FileAccessI() löst eine IOException aus
{
this("",0);
}
public FileAccessI(String sName,long nPos) löst eine IOException aus
{
oSavedFile = new RandomAccessFile(sName,"rw");
this.nPos = nPos;
oSavedFile.seek(nPos);
}
öffentlich synchronisiertes int write(byte[] b,int nStart,int nLen)
{
int n = -1;
versuchen{
oSavedFile.write(b,nStart,nLen);
n = nLen;
}
Catch(IOException e)
{
e.printStackTrace();
}
Rückkehr n;
}
}
/*
**SiteInfoBean.java
*/
Paket NetFox;
öffentliche Klasse SiteInfoBean {
private String sSiteURL; //URL der Site
private String sFilePath; //Pfad der gespeicherten Datei
private String sFileName; //Name der gespeicherten Datei
private int nSplitter; //Anzahl der geteilten Download-Dateien
öffentliches SiteInfoBean()
{
//Standardwert von nSplitter ist 5
this("","","",5);
}
public SiteInfoBean(String sURL,String sPath,String sName,int nSpiltter)
{
sSiteURL= sURL;
sFilePath = sPath;
sFileName = sName;
this.nSplitter = nSpiltter;
}
öffentlicher String getSSiteURL()
{
sSiteURL zurückgeben;
}
public void setSSiteURL(String-Wert)
{
sSiteURL = Wert;
}
öffentlicher String getSFilePath()
{
return sFilePath;
}
public void setSFilePath(String-Wert)
{
sFilePath = Wert;
}
öffentlicher String getSFileName()
{
return sFileName;
}
public void setSFileName(String-Wert)
{
sFileName = value;
}
public int getNSplitter()
{
return nSplitter;
}
public void setNSsplitter(int nCount)
{
nSplitter = nCount;
}
}
/*
**Dienstprogramm.java
*/
Paket NetFox;
öffentliches Klassendienstprogramm {
öffentliches Dienstprogramm()
{
}
öffentlicher statischer Leerschlaf (int nSecond)
{
versuchen{
Thread.sleep(nSecond);
}
Catch (Ausnahme e)
{
e.printStackTrace();
}
}
öffentliches statisches Void-Protokoll (String sMsg)
{
System.err.println(sMsg);
}
öffentliches statisches Void-Protokoll (int sMsg)
{
System.err.println(sMsg);
}
}
/*
**TestMethod.java
*/
Paket NetFox;
öffentliche Klasse TestMethod {
öffentliche TestMethod()
{ ///xx/weblogic60b2_win.exe
versuchen{
SiteInfoBean bean = new SiteInfoBean("http://localhost/xx/weblogic60b2_win.exe","L://temp","weblogic60b2_win.exe",5);
//SiteInfoBean bean = new SiteInfoBean("http://localhost:8080/down.zip","L://temp","weblogic60b2_win.exe",5);
SiteFileFetch fileFetch = new SiteFileFetch(bean);
fileFetch.start();
}
Catch(Exception e){e.printStackTrace ();
}
}
public static void main(String[] args)
{
neue TestMethode();
}
}