(1) Le principe du téléchargement avec reprise <BR>En fait, le principe du téléchargement avec reprise est très simple. C'est juste que la requête HTTP est différente du téléchargement général.
Par exemple, lorsqu'un navigateur demande un fichier sur le serveur, la requête qu'il effectue est la suivante :
Supposons que le nom de domaine du serveur est wwww.sjtu.edu.cn et que le nom du fichier est down.zip.
OBTENIR /down.zip HTTP/1.1
Accepter : image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-
excel, application/msword, application/vnd.ms-powerpoint, */*
Langue d'acceptation : zh-cn
Accepter l'encodage : gzip, dégonfler
Agent utilisateur : Mozilla/4.0 (compatible ; MSIE 5.01 ; Windows NT 5.0)
Connexion : Keep-Alive
Une fois que le serveur a reçu la demande, il recherche le fichier demandé comme requis, extrait les informations du fichier, puis les renvoie au navigateur. Les informations renvoyées sont les suivantes.
200
Longueur du contenu = 106786028
Accepter-Ranges=octets
Date = lundi 30 avril 2001 12:56:11 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Serveur=Microsoft-IIS/5.0
Dernière modification = lundi 30 avril 2001 12:56:11 GMT
Ce que l'on appelle la reprise du téléchargement signifie continuer le téléchargement à partir du point où le fichier a été téléchargé. Donc dans le navigateur client, passez
En ce qui concerne les serveurs Web, une information supplémentaire doit être ajoutée : par où commencer.
Ce qui suit est un "navigateur" compilé par moi-même pour transmettre les informations de la requête au serveur Web. La requête commence à 2 000 070 octets.
OBTENIR /down.zip HTTP/1.0
Agent utilisateur : NetFox
PLAGE : octets = 2000070-
Accepter : texte/html, image/gif, image/jpeg, * ; q=.2, */* ;
Si vous regardez attentivement, vous trouverez une ligne supplémentaire de RANGE : bytes=2000070-
Le sens de cette ligne est d'indiquer au serveur que le fichier down.zip sera transmis à partir de 2000070 octets, et que les octets précédents n'ont pas besoin d'être transmis.
Une fois que le serveur a reçu cette requête, les informations renvoyées sont les suivantes :
206
Longueur du contenu = 106786028
Content-Range=octets 2000070-106786027/106786028
Date = lundi 30 avril 2001 12:55:20 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Serveur=Microsoft-IIS/5.0
Dernière modification = lundi 30 avril 2001 12:55:20 GMT
En la comparant avec les informations renvoyées par le serveur précédent, vous constaterez qu'une ligne a été ajoutée :
Content-Range=octets 2000070-106786027/106786028
Le code retour a également été modifié en 206 au lieu de 200.
Connaissant les principes ci-dessus, vous pouvez programmer le téléchargement de la reprise du point d'arrêt.
(2) Points clés de l'implémentation du transfert de reprise du point d'arrêt en Java
(1) Quelle méthode est utilisée pour soumettre RANGE : bytes=2000070-.
Bien sûr, cela peut certainement être fait en utilisant le Socket d'origine, mais ce serait trop gênant. En fait, le package net de Java fournit cette fonction. Le code est le suivant :
URL url = nouvelle URL("http://www.sjtu.edu.cn/down.zip");
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();
//Définir l'agent utilisateur
httpConnection.setRequestProperty("User-Agent","NetFox");
//Définir la position de départ de la transmission de reprise du point d'arrêt
httpConnection.setRequestProperty("RANGE","bytes=2000070");
//Obtenir le flux d'entrée
Entrée InputStream = httpConnection.getInputStream();
Le flux d'octets extrait du flux d'entrée est le flux d'octets commençant à 2000070 dans le fichier down.zip.
Vous voyez, il est en fait très simple d'implémenter le transfert de reprise du point d'arrêt en Java.
La prochaine chose à faire est de savoir comment enregistrer le flux obtenu dans un fichier.
La méthode utilisée pour enregistrer le fichier.
J'utilise la classe RandAccessFile dans le package IO.
L'opération est assez simple. Supposons que le fichier soit enregistré à partir de 2000070. Le code est le suivant :
Copiez le code comme suit :
RandomAccess oSavedFile = new RandomAccessFile("down.zip","rw");
nPos long = 2000070 ;
// Localisez le pointeur de fichier sur la position nPos
oSavedFile.seek(nPos);
octet[] b = nouvel octet[1024];
int nLire ;
//Lire le flux d'octets du flux d'entrée et l'écrire dans le fichier
while((nRead=input.read(b,0,1024)) > 0)
{
oSavedFile.write(b,0,nRead);
}
Quoi qu'il en soit, c'est très simple.
La prochaine chose à faire est de l'intégrer dans un programme complet. Y compris une série de contrôles de threads, etc.
(3) L'implémentation du noyau de reprise de point d'arrêt <BR>utilise principalement 6 classes, dont une classe de test.
SiteFileFetch.java est responsable de la récupération de l'intégralité du fichier et du contrôle du thread interne (classe FileSplitterFetch).
FileSplitterFetch.java est responsable de la récupération de certains fichiers.
FileAccess.java est responsable du stockage des fichiers.
Informations sur le fichier que SiteInfoBean.java souhaite analyser, telles que le répertoire dans lequel le fichier est enregistré, son nom, l'URL du fichier analysé, etc.
Classe d'outils Utility.java, mettez quelques méthodes simples.
Classe de test TestMethod.java.
Voici le programme source :
Copiez le code comme suit :
/*
**SiteFileFetch.java
*/
paquet NetFox ;
importer java.io.* ;
importer java.net.* ;
la classe publique SiteFileFetch étend le fil {
SiteInfoBean siteInfoBean = null; //Informations sur le fichier Bean
long[] nStartPos; //Position de départ
long[] nEndPos; //Position finale
FileSplitterFetch[] fileSplitterFetch; //Objet sous-thread
long nFileLength; //Longueur du fichier
boolean bFirst = true; //S'il faut récupérer le fichier pour la première fois
booléen bStop = false; //Signe d'arrêt
File tmpFile; //Informations temporaires pour le téléchargement du fichier
Sortie DataOutputStream ; // Flux de sortie vers un fichier
public SiteFileFetch (bean SiteInfoBean) lance IOException
{
siteInfoBean = bean ;
//tmpFile = File.createTempFile ("zhong","1111",new File(bean.getSFilePath()));
tmpFile = new File(bean.getSFilePath()+File.separator + bean.getSFileName()+".info");
si (tmpFile.exists ())
{
bPremier = faux ;
read_nPos();
}
autre
{
nStartPos = new long[bean.getNSplitter()];
nEndPos = nouveau long[bean.getNSplitter()];
}
}
exécution publique vide()
{
//Obtenir la longueur du fichier
//diviser le fichier
//Instance FileSplitterFetch
//Démarre le fil FileSplitterFetch
//Attendez que le thread enfant revienne
essayer{
si(bPremier)
{
nFileLength = getFileSize();
si (nFileLength == -1)
{
System.err.println("La longueur du fichier n'est pas connue !");
}
sinon si (nFileLength == -2)
{
System.err.println("Le fichier n'est pas accessible !");
}
autre
{
pour(int i=0;i<nStartPos.length;i++)
{
nStartPos[i] = (long)(i*(nFileLength/nStartPos.length));
}
pour(int i=0;i<nEndPos.length-1;i++)
{
nEndPos[i] = nStartPos[i+1];
}
nEndPos[nEndPos.length-1] = nFileLength;
}
}
//Démarre le fil enfant
fileSplitterFetch = nouveau FileSplitterFetch[nStartPos.length];
pour(int i=0;i<nStartPos.length;i++)
{
fileSplitterFetch[i] = nouveau 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] = nouveau 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();
// Attendez la fin du thread enfant
//int compte = 0;
//S'il faut terminer la boucle while
booléen breakWhile = faux ;
pendant que(!bStop)
{
write_nPos();
Utilitaire.sleep(500);
breakWhile = vrai ;
pour(int i=0;i<nStartPos.length;i++)
{
si(!fileSplitterFetch[i].bDownOver)
{
breakWhile = faux ;
casser;
}
}
si (pausePendant)
casser;
//compte++;
//si(nombre>4)
//siteStop();
}
System.err.println("Téléchargement du fichier terminé !");
}
catch(Exception e){e.printStackTrace ();}
}
//Obtenir la longueur du fichier
public longgetFileSize()
{
int nFileLength = -1;
essayer{
URL url = nouvelle URL (siteInfoBean.getSSiteURL());
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();
httpConnection.setRequestProperty("User-Agent","NetFox");
int réponseCode=httpConnection.getResponseCode();
si (code de réponse> = 400)
{
processErrorCode(responseCode);
return -2 ; //-2 représente l'accès est une erreur
}
En-tête de chaîne ;
pour(int i=1;;i++)
{
//DataInputStream in = new DataInputStream(httpConnection.getInputStream ());
//Utility.log(in.readLine());
sHeader=httpConnection.getHeaderFieldKey(i);
si(sEn-Tête!=null)
{
if(sHeader.equals("Content-Length"))
{
nFileLength = Integer.parseInt(httpConnection.getHeaderField(sHeader));
casser;
}
}
autre
casser;
}
}
catch(IOException e){e.printStackTrace ();}
catch(Exception e){e.printStackTrace ();}
Utilitaire.log(nFileLength);
retourner nFileLength;
}
//Enregistrer les informations de téléchargement (position du pointeur du fichier)
privé vide write_nPos()
{
essayer{
sortie = nouveau DataOutputStream(nouveau FileOutputStream(tmpFile));
sortie.writeInt(nStartPos.length);
pour(int i=0;i<nStartPos.length;i++)
{
// sortie.writeLong(nPos[i]);
sortie.writeLong(fileSplitterFetch[i].nStartPos);
sortie.writeLong(fileSplitterFetch[i].nEndPos);
}
sortie.close();
}
catch(IOException e){e.printStackTrace ();}
catch(Exception e){e.printStackTrace ();}
}
//Lire les informations de téléchargement enregistrées (position du pointeur du fichier)
vide privé read_nPos()
{
essayer{
Entrée DataInputStream = new DataInputStream(new FileInputStream(tmpFile));
int nCount = input.readInt();
nStartPos = nouveau long[nCount] ;
nEndPos = nouveau long[nCount] ;
pour(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 ();}
}
processus vide privéErrorCode (int nErrorCode)
{
System.err.println("Code d'erreur : " + nErrorCode);
}
//Arrêter le téléchargement du fichier
site vide publicStop()
{
bArrêter = vrai ;
pour(int i=0;i<nStartPos.length;i++)
fileSplitterFetch[i].splitterStop();
}
}
/*
**FileSplitterFetch.java
*/
paquet NetFox ;
importer java.io.* ;
importer java.net.* ;
la classe publique FileSplitterFetch étend le fil {
URL de chaîne ; //URL du fichier
long nStartPos ; //Position de début de l'extrait de fichier
long nEndPos ; //Position de fin de l'extrait de fichier
int nThreadID; //ID du fil de discussion
boolean bDownOver = false; //La descente est terminée
boolean bStop = false; //Arrêt identique
FileAccessI fileAccessI = null; //Interface d'accès aux fichiers
public FileSplitterFetch (String sURL, String sName, long nStart, long nEnd, int id) lance IOException
{
this.sURL = sURL;
this.nStartPos = nStart;
this.nEndPos = nEnd;
nThreadID = identifiant ;
fileAccessI = nouveau FileAccessI(sName,nStartPos);
}
exécution publique vide()
{
pendant que(nStartPos < nEndPos && !bStop)
{
essayer{
URL url = nouvelle URL (sURL);
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();
httpConnection.setRequestProperty("User-Agent","NetFox");
Chaîne sProperty = "bytes="+nStartPos+"-";
httpConnection.setRequestProperty("RANGE",sProperty);
Utility.log(sPropriété);
Entrée InputStream = httpConnection.getInputStream();
//logResponseHead(httpConnection);
octet[] b = nouvel octet[1024];
int nLire ;
while((nRead=input.read(b,0,1024)) > 0 && nStartPos < nEndPos && !bStop)
{
nStartPos += fileAccessI.write(b,0,nRead);
//si(nThreadID == 1)
// Utility.log("nStartPos = " + nStartPos + ", nEndPos = " + nEndPos);
}
Utility.log("Thread " + nThreadID + " est terminé !");
bDownOver = vrai ;
//nPos = fileAccessI.write (b,0,nRead);
}
catch(Exception e){e.printStackTrace ();}
}
}
// Imprimer les informations d'en-tête de réponse
public void logResponseHead (HttpURLConnection avec)
{
pour(int i=1;;i++)
{
En-tête de chaîne=con.getHeaderFieldKey(i);
si(en-tête!=null)
//responseHeaders.put(header,httpConnection.getHeaderField(header));
Utility.log(header+" : "+con.getHeaderField(header));
autre
casser;
}
}
public void splitterStop()
{
bArrêter = vrai ;
}
}
/*
**FileAccess.java
*/
paquet NetFox ;
importer java.io.* ;
classe publique FileAccessI implémente Serialisable{
RandomAccessFile oSavedFile ;
nPos longs ;
public FileAccessI() lance IOException
{
ceci("",0);
}
public FileAccessI (String sName, long nPos) lève IOException
{
oSavedFile = nouveau RandomAccessFile(sName,"rw");
this.nPos = nPos;
oSavedFile.seek(nPos);
}
écriture int synchronisée publique (octet [] b, int nStart, int nLen)
{
entier n = -1 ;
essayer{
oSavedFile.write(b,nStart,nLen);
n = nLen;
}
capture (IOException e)
{
e.printStackTrace();
}
retourner n ;
}
}
/*
**SiteInfoBean.java
*/
paquet NetFox ;
classe publique SiteInfoBean {
chaîne privée sSiteURL ; //URL du site
chaîne privée sFilePath ; //Chemin du fichier enregistré
private String sFileName ; //Nom du fichier enregistré
private int nSplitter ; // Nombre de fichiers téléchargés divisés
SiteInfoBean public()
{
//la valeur par défaut de nSplitter est 5
ceci("","","",5);
}
public SiteInfoBean (String sURL, String sPath, String sName, int nSpiltter)
{
sSiteURL= sURL ;
sCheminFichier = sChemin ;
sNomFichier = sNom ;
this.nSplitter = nSpiltter;
}
chaîne publique getSSiteURL()
{
renvoie l'URL du site ;
}
public void setSSiteURL (valeur de chaîne)
{
sSiteURL = valeur ;
}
chaîne publique getSFilePath()
{
retourner sFilePath ;
}
public void setSFilePath (valeur de chaîne)
{
sFilePath = valeur ;
}
chaîne publique getSFileName()
{
renvoie sFileName ;
}
public void setSFileName (valeur de chaîne)
{
sFileName = valeur ;
}
public int getNSplitter()
{
renvoyer nSplitter ;
}
public void setNSplitter (int nCount)
{
nSplitter = nCount;
}
}
/*
**Utilitaire.java
*/
paquet NetFox ;
Utilitaire de classe publique {
utilité publique()
{
}
sommeil vide statique public (int nSecond)
{
essayer{
Thread.sleep(nSecond);
}
attraper (Exception e)
{
e.printStackTrace();
}
}
journal vide statique public (String sMsg)
{
System.err.println(sMsg);
}
journal vide statique public (int sMsg)
{
System.err.println(sMsg);
}
}
/*
**TestMethod.java
*/
paquet NetFox ;
classe publique TestMethod {
méthode de test publique ()
{ ///xx/weblogic60b2_win.exe
essayer{
Bean SiteInfoBean = new SiteInfoBean("http://localhost/xx/weblogic60b2_win.exe","L://temp","weblogic60b2_win.exe",5);
//Bean SiteInfoBean = 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[] arguments)
{
new TestMethod();
}
}